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:
@@ -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
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user