feat: Show a preview of what's in the user's clipboard (#32132)

This also fixes Studio container view 404.
This commit is contained in:
Braden MacDonald
2023-05-05 08:35:03 -07:00
committed by GitHub
parent c34f8efc0e
commit 4b72194b98
11 changed files with 191 additions and 11 deletions

View File

@@ -59,7 +59,7 @@ def get_user_clipboard_json(user_id: int, request: HttpRequest = None):
clipboard = _UserClipboard.objects.get(user_id=user_id)
except _UserClipboard.DoesNotExist:
# This user does not have any content on their clipboard.
return {"content": None, "source_usage_key": "", "source_context_title": ""}
return {"content": None, "source_usage_key": "", "source_context_title": "", "source_edit_url": ""}
serializer = _UserClipboardSerializer(clipboard, context={'request': request})
return serializer.data

View File

@@ -3,6 +3,10 @@ Serializers for the content libraries REST API
"""
from rest_framework import serializers
from cms.djangoapps.contentstore.views.helpers import xblock_studio_url, xblock_type_display_name
from common.djangoapps.student.auth import has_studio_read_access
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError
from .models import StagedContent
@@ -11,6 +15,7 @@ class StagedContentSerializer(serializers.ModelSerializer):
Serializer for staged content. Doesn't include the OLX by default.
"""
olx_url = serializers.HyperlinkedIdentityField(view_name="staged-content-olx", lookup_field="id")
block_type_display = serializers.SerializerMethodField(source="get_block_type_display")
class Meta:
model = StagedContent
@@ -21,11 +26,16 @@ class StagedContentSerializer(serializers.ModelSerializer):
'purpose',
'status',
'block_type',
'block_type_display',
# We don't include OLX; it may be large. But we include the URL to retrieve it.
'olx_url',
'display_name',
]
def get_block_type_display(self, obj):
""" Get the friendly name for this XBlock/component type """
return xblock_type_display_name(obj.block_type)
class UserClipboardSerializer(serializers.Serializer):
"""
@@ -35,6 +45,27 @@ class UserClipboardSerializer(serializers.Serializer):
source_usage_key = serializers.CharField(allow_blank=True)
# The title of the course that the content came from originally, if relevant
source_context_title = serializers.CharField(allow_blank=True, source="get_source_context_title")
# The URL where the original content can be seen, if it still exists and the current user can view it
source_edit_url = serializers.SerializerMethodField(source="get_source_edit_url")
def get_source_edit_url(self, obj) -> str:
""" Get the URL where the user can edit the given XBlock, if it exists """
request = self.context.get("request", None)
user = request.user if request else None
if not user:
return ""
if not obj.source_usage_key.context_key.is_course:
return "" # Linking back to libraries is not implemented yet
if not has_studio_read_access(user, obj.source_usage_key.course_key):
return ""
try:
block = modulestore().get_item(obj.source_usage_key)
except ItemNotFoundError:
return ""
edit_url = xblock_studio_url(block, find_parent=True)
if edit_url:
return request.build_absolute_uri(edit_url)
return ""
class PostToClipboardSerializer(serializers.Serializer):

View File

@@ -45,7 +45,8 @@ class ClipboardTestCase(ModuleStoreTestCase):
self.assertEqual(response.json(), {
"content": None,
"source_usage_key": "",
"source_context_title": ""
"source_context_title": "",
"source_edit_url": "",
})
## The Python method for getting the API response should be identical:
self.assertEqual(
@@ -86,6 +87,7 @@ class ClipboardTestCase(ModuleStoreTestCase):
self.assertEqual(response_data["source_context_title"], "Toy Course")
self.assertEqual(response_data["content"], {**response_data["content"], **{
"block_type": "video",
"block_type_display": "Video",
# To ensure API stability, we are hard-coding these expected values:
"purpose": "clipboard",
"status": "ready",
@@ -190,6 +192,7 @@ class ClipboardTestCase(ModuleStoreTestCase):
html_clip_data = response.json()
self.assertEqual(html_clip_data["source_usage_key"], str(html_key))
self.assertEqual(html_clip_data["content"]["block_type"], "html")
self.assertEqual(html_clip_data["content"]["block_type_display"], "Text")
## The Python method for getting the API response should be identical:
self.assertEqual(html_clip_data, python_api.get_user_clipboard_json(self.user.id, response.wsgi_request))

View File

@@ -74,7 +74,12 @@ class ClipboardEndpoint(APIView):
clipboard = UserClipboard.objects.get(user=request.user.id)
except UserClipboard.DoesNotExist:
# This user does not have any content on their clipboard.
return Response({"content": None, "source_usage_key": "", "source_context_title": ""})
return Response({
"content": None,
"source_usage_key": "",
"source_context_title": "",
"source_edit_url": "",
})
serializer = UserClipboardSerializer(clipboard, context={"request": request})
return Response(serializer.data)