This commit is contained in:
Markos Gogoulos
2026-04-20 09:46:39 +03:00
parent 1261719996
commit 9539741d11
8 changed files with 66 additions and 19 deletions
+4
View File
@@ -79,6 +79,10 @@ urlpatterns = [
rf"^api/v1/media/{friendly_token}/trim_video$", rf"^api/v1/media/{friendly_token}/trim_video$",
views.trim_video, views.trim_video,
), ),
re_path(
rf"^api/v1/media/{friendly_token}/share$",
views.MediaShare.as_view(),
),
re_path(r"^api/v1/categories$", views.CategoryList.as_view()), re_path(r"^api/v1/categories$", views.CategoryList.as_view()),
re_path(r"^api/v1/categories/contributor$", views.CategoryListContributor.as_view()), re_path(r"^api/v1/categories/contributor$", views.CategoryListContributor.as_view()),
re_path(r"^api/v1/tags$", views.TagList.as_view()), re_path(r"^api/v1/tags$", views.TagList.as_view()),
+21
View File
@@ -1212,3 +1212,24 @@ class MediaSearch(APIView):
page = paginator.paginate_queryset(media, request) page = paginator.paginate_queryset(media, request)
serializer = MediaSearchSerializer(page, many=True, context={"request": request}) serializer = MediaSearchSerializer(page, many=True, context={"request": request})
return paginator.get_paginated_response(serializer.data) return paginator.get_paginated_response(serializer.data)
class MediaShare(APIView):
"""Create a self-referential MediaPermission to mark a media as shared."""
def post(self, request, friendly_token):
if not request.user.is_authenticated:
return Response({'error': 'Authentication required'}, status=status.HTTP_401_UNAUTHORIZED)
media = get_object_or_404(Media, friendly_token=friendly_token)
if media.user != request.user and not is_mediacms_editor(request.user):
return Response({'error': 'Permission denied'}, status=status.HTTP_403_FORBIDDEN)
MediaPermission.objects.get_or_create(
media=media,
user=request.user,
defaults={'owner_user': request.user, 'permission': 'owner'},
)
return Response({'status': 'ok'})
@@ -159,12 +159,15 @@ class ProfileMediaPage extends Page {
handleMediaSelection(mediaId, isSelected) { handleMediaSelection(mediaId, isSelected) {
// Only used in select media embed mode; normal mode is handled by bulkActions // Only used in select media embed mode; normal mode is handled by bulkActions
this.setState((prevState) => { this.setState(() => {
const newSelectedMedia = new Set(); const newSelectedMedia = new Set();
if (isSelected) { if (isSelected) {
newSelectedMedia.add(mediaId); newSelectedMedia.add(mediaId);
}
return { selectedMedia: newSelectedMedia };
});
if (isSelected) {
if (window.parent !== window) { if (window.parent !== window) {
const baseUrl = window.location.origin; const baseUrl = window.location.origin;
const embedUrl = `${baseUrl}/embed?m=${mediaId}`; const embedUrl = `${baseUrl}/embed?m=${mediaId}`;
@@ -172,13 +175,19 @@ class ProfileMediaPage extends Page {
window.parent.postMessage({ window.parent.postMessage({
type: 'videoSelected', type: 'videoSelected',
embedUrl: embedUrl, embedUrl: embedUrl,
videoId: mediaId videoId: mediaId,
}, '*'); }, '*');
} }
}
return { selectedMedia: newSelectedMedia }; // Mark media as shared so LTI users in the course can access it
}); fetch(`/api/v1/media/${mediaId}/share`, {
method: 'POST',
headers: {
'X-CSRFToken': this.props.bulkActions.getCsrfToken(),
'Content-Type': 'application/json',
},
}).catch(() => {});
}
} }
onToggleFiltersClick() { onToggleFiltersClick() {
+14 -1
View File
@@ -700,7 +700,8 @@ class EmbedMediaLTIView(View):
if lti_session and request.user.is_authenticated: if lti_session and request.user.is_authenticated:
context_id = lti_session.get('context_id') context_id = lti_session.get('context_id')
platform_id = lti_session.get('platform_id') platform_id = lti_session.get('platform_id')
if context_id and platform_id:
if media.is_shared and context_id and platform_id:
try: try:
resource_link = ( resource_link = (
LTIResourceLink.objects.filter( LTIResourceLink.objects.filter(
@@ -728,6 +729,18 @@ class EmbedMediaLTIView(View):
except Exception: except Exception:
logger.exception('EmbedMediaLTIView: error checking course access for user=%s media=%s', request.user, friendly_token) logger.exception('EmbedMediaLTIView: error checking course access for user=%s media=%s', request.user, friendly_token)
if not can_view and media.state == 'private':
has_rbac_access = media.category.filter(
is_rbac_category=True,
rbac_groups__members=request.user,
).exists()
has_direct_permission = MediaPermission.objects.filter(
media=media,
user=request.user,
).exists()
if has_rbac_access or has_direct_permission:
can_view = True
if not can_view and media.state in ["public", "unlisted"]: if not can_view and media.state in ["public", "unlisted"]:
can_view = True can_view = True
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long