diff --git a/lti/deep_linking.py b/lti/deep_linking.py index 044dfc33..c25fccb0 100644 --- a/lti/deep_linking.py +++ b/lti/deep_linking.py @@ -69,55 +69,41 @@ class SelectMediaView(View): def post(self, request): """Return selected media as deep linking content items""" - print("=" * 80) - print("DEEP LINKING - MEDIA SELECTION SUBMITTED") - print("=" * 80) - - # Get deep link session data deep_link_data = request.session.get('lti_deep_link') if not deep_link_data: - print("ERROR: No deep link session data found") return JsonResponse({'error': 'Invalid session'}, status=400) - print(f"Deep link data: {deep_link_data}") - - # Get selected media IDs selected_ids = request.POST.getlist('media_ids[]') - print(f"Selected media IDs: {selected_ids}") if not selected_ids: - print("ERROR: No media selected") return JsonResponse({'error': 'No media selected'}, status=400) - # Build content items content_items = [] for media_id in selected_ids: try: media = Media.objects.get(id=media_id) - # Build embed URL - embed_url = request.build_absolute_uri(reverse('lti:embed_media', args=[media.friendly_token])) + # Build launch URL (must be an LTI launch endpoint that handles POST with id_token) + # The /lti/launch/ endpoint will use the custom parameter to redirect to the correct media + launch_url = request.build_absolute_uri(reverse('lti:launch')) content_item = { 'type': 'ltiResourceLink', 'title': media.title, - 'url': embed_url, + 'url': launch_url, 'custom': { 'media_friendly_token': media.friendly_token, }, } - # Add thumbnail if available if media.thumbnail_url: - # Ensure thumbnail URL is absolute thumbnail_url = media.thumbnail_url if not thumbnail_url.startswith('http'): thumbnail_url = request.build_absolute_uri(thumbnail_url) content_item['thumbnail'] = {'url': thumbnail_url, 'width': 344, 'height': 194} - # Add iframe configuration content_item['iframe'] = {'width': 960, 'height': 540} content_items.append(content_item) @@ -126,26 +112,16 @@ class SelectMediaView(View): continue if not content_items: - print("ERROR: No valid media found after processing") return JsonResponse({'error': 'No valid media found'}, status=400) - print(f"Built {len(content_items)} content items") - - # Create deep linking JWT response - # Note: This is a simplified version # Full implementation would use PyLTI1p3's DeepLink response builder - print("Creating deep linking JWT...") jwt_response = self.create_deep_link_jwt(deep_link_data, content_items, request) - print(f"JWT created successfully (length: {len(jwt_response)})") - print(f"JWT (first 100 chars): {jwt_response[:100]}...") - # Return auto-submit form that posts JWT back to Moodle context = { 'return_url': deep_link_data['deep_link_return_url'], 'jwt': jwt_response, } - print(f"Returning to Moodle at: {deep_link_data['deep_link_return_url']}") return render(request, 'lti/deep_link_return.html', context) def create_deep_link_jwt(self, deep_link_data, content_items, request): @@ -153,34 +129,20 @@ class SelectMediaView(View): Create JWT response for deep linking - manual implementation """ try: - print("=" * 80) - print("CREATING DEEP LINKING JWT") - print("=" * 80) - platform_id = deep_link_data['platform_id'] platform = LTIPlatform.objects.get(id=platform_id) deployment_id = deep_link_data['deployment_id'] message_launch_data = deep_link_data['message_launch_data'] - print(f"Platform: {platform.name}") - print(f"Platform ID: {platform.platform_id}") - print(f"Client ID: {platform.client_id}") - print(f"Deployment ID: {deployment_id}") - - # Get deep linking settings from original launch data deep_linking_settings = message_launch_data.get('https://purl.imsglobal.org/spec/lti-dl/claim/deep_linking_settings', {}) - print(f"Deep linking settings: {deep_linking_settings}") - # Get tool's private key for signing key_obj = LTIToolKeys.get_or_create_keys() jwk_obj = jwk.JWK(**key_obj.private_key_jwk) pem_bytes = jwk_obj.export_to_pem(private_key=True, password=None) private_key = serialization.load_pem_private_key(pem_bytes, password=None, backend=default_backend()) - # Build JWT payload according to LTI Deep Linking spec now = int(time.time()) - # Convert content_items to LTI content item format lti_content_items = [] for item in content_items: lti_item = { @@ -200,19 +162,14 @@ class SelectMediaView(View): lti_content_items.append(lti_item) - # Create JWT payload - # Use client_id as issuer to avoid "wrong consumer key" errors in Moodle tool_issuer = platform.client_id - # Per LTI spec, aud should be the platform's issuer URL - # Try just the platform URL (some Moodle versions don't accept arrays) audience = platform.platform_id - # Get sub (subject) from original launch sub = message_launch_data.get('sub') payload = { - 'iss': tool_issuer, # Tool's issuer (MediaCMS URL) + 'iss': tool_issuer, 'aud': audience, 'exp': now + 3600, 'iat': now, @@ -223,33 +180,17 @@ class SelectMediaView(View): 'https://purl.imsglobal.org/spec/lti-dl/claim/content_items': lti_content_items, } - # Add sub claim if present in original request if sub: payload['sub'] = sub - # Echo back data claim if it was present in the request if 'data' in deep_linking_settings: payload['https://purl.imsglobal.org/spec/lti-dl/claim/data'] = deep_linking_settings['data'] - print("JWT Payload:") - print(f" iss (issuer): {tool_issuer}") - print(f" aud (audience): {audience}") - print(f" deployment_id: {deployment_id} (type: {type(deployment_id).__name__})") - print(f" content_items count: {len(lti_content_items)}") - print(" Full payload:") - import json - - print(json.dumps(payload, indent=2, default=str)) - - # Sign JWT with tool's private key kid = key_obj.private_key_jwk['kid'] - print(f"Signing JWT with kid: {kid}") response_jwt = jwt.encode(payload, private_key, algorithm='RS256', headers={'kid': kid}) - print("JWT signed successfully") return response_jwt except Exception as e: - # Log error for debugging traceback.print_exc() raise ValueError(f"Failed to create Deep Linking JWT: {str(e)}")