mirror of
https://github.com/mediacms-io/mediacms.git
synced 2025-11-21 05:56:03 -05:00
Bulk actions support (#1418)
This commit is contained in:
@@ -2,7 +2,7 @@ from datetime import datetime, timedelta
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.postgres.search import SearchQuery
|
||||
from django.db.models import Q
|
||||
from django.db.models import Count, Q
|
||||
from django.shortcuts import get_object_or_404
|
||||
from drf_yasg import openapi
|
||||
from drf_yasg.utils import swagger_auto_schema
|
||||
@@ -33,7 +33,15 @@ from ..methods import (
|
||||
show_related_media,
|
||||
update_user_ratings,
|
||||
)
|
||||
from ..models import EncodeProfile, Media, MediaPermission, Playlist, PlaylistMedia
|
||||
from ..models import (
|
||||
Category,
|
||||
EncodeProfile,
|
||||
Media,
|
||||
MediaPermission,
|
||||
Playlist,
|
||||
PlaylistMedia,
|
||||
Tag,
|
||||
)
|
||||
from ..serializers import MediaSearchSerializer, MediaSerializer, SingleMediaSerializer
|
||||
from ..stop_words import STOP_WORDS
|
||||
from ..tasks import save_user_action
|
||||
@@ -61,15 +69,13 @@ class MediaList(APIView):
|
||||
if user:
|
||||
base_filters &= Q(user=user)
|
||||
|
||||
base_queryset = Media.objects.prefetch_related("user")
|
||||
base_queryset = Media.objects.prefetch_related("user", "tags")
|
||||
|
||||
if not request.user.is_authenticated:
|
||||
return base_queryset.filter(base_filters).order_by("-add_date")
|
||||
return base_queryset.filter(base_filters)
|
||||
|
||||
# Build OR conditions for authenticated users
|
||||
conditions = base_filters # Start with listable media
|
||||
conditions = base_filters
|
||||
|
||||
# Add user permissions
|
||||
permission_filter = {'user': request.user}
|
||||
if user:
|
||||
permission_filter['owner_user'] = user
|
||||
@@ -80,7 +86,6 @@ class MediaList(APIView):
|
||||
perm_conditions &= Q(user=user)
|
||||
conditions |= perm_conditions
|
||||
|
||||
# Add RBAC conditions
|
||||
if getattr(settings, 'USE_RBAC', False):
|
||||
rbac_categories = request.user.get_rbac_categories_as_member()
|
||||
rbac_conditions = Q(category__in=rbac_categories)
|
||||
@@ -88,10 +93,9 @@ class MediaList(APIView):
|
||||
rbac_conditions &= Q(user=user)
|
||||
conditions |= rbac_conditions
|
||||
|
||||
return base_queryset.filter(conditions).distinct().order_by("-add_date")[:1000]
|
||||
return base_queryset.filter(conditions).distinct()
|
||||
|
||||
def get(self, request, format=None):
|
||||
# Show media
|
||||
# authenticated users can see:
|
||||
|
||||
# All listable media (public access)
|
||||
@@ -100,51 +104,151 @@ class MediaList(APIView):
|
||||
|
||||
params = self.request.query_params
|
||||
show_param = params.get("show", "")
|
||||
|
||||
author_param = params.get("author", "").strip()
|
||||
tag = params.get("t", "").strip()
|
||||
ordering = params.get("ordering", "").strip()
|
||||
sort_by = params.get("sort_by", "").strip()
|
||||
media_type = params.get("media_type", "").strip()
|
||||
upload_date = params.get('upload_date', '').strip()
|
||||
duration = params.get('duration', '').strip()
|
||||
publish_state = params.get('publish_state', '').strip()
|
||||
query = params.get("q", "").strip().lower()
|
||||
|
||||
parsed_combined = False
|
||||
if sort_by and '_' in sort_by:
|
||||
parts = sort_by.rsplit('_', 1)
|
||||
if len(parts) == 2 and parts[1] in ['asc', 'desc']:
|
||||
field, direction = parts
|
||||
if field in ["title", "add_date", "edit_date", "views", "likes"]:
|
||||
sort_by = field
|
||||
ordering = "" if direction == "asc" else "-"
|
||||
parsed_combined = True
|
||||
|
||||
# Fall back to legacy handling only if we didn't parse a combined option
|
||||
if not parsed_combined:
|
||||
sort_by_options = ["title", "add_date", "edit_date", "views", "likes"]
|
||||
if sort_by not in sort_by_options:
|
||||
sort_by = "add_date"
|
||||
if ordering == "asc":
|
||||
ordering = ""
|
||||
else:
|
||||
ordering = "-"
|
||||
|
||||
if media_type not in ["video", "image", "audio", "pdf"]:
|
||||
media_type = None
|
||||
|
||||
gte = None
|
||||
if upload_date:
|
||||
if upload_date == 'today':
|
||||
gte = datetime.now().date()
|
||||
if upload_date == 'this_week':
|
||||
gte = datetime.now() - timedelta(days=7)
|
||||
if upload_date == 'this_month':
|
||||
year = datetime.now().date().year
|
||||
month = datetime.now().date().month
|
||||
gte = datetime(year, month, 1)
|
||||
if upload_date == 'this_year':
|
||||
year = datetime.now().date().year
|
||||
gte = datetime(year, 1, 1)
|
||||
|
||||
already_sorted = False
|
||||
pagination_class = api_settings.DEFAULT_PAGINATION_CLASS
|
||||
|
||||
if show_param == "recommended":
|
||||
pagination_class = FastPaginationWithoutCount
|
||||
media = show_recommended_media(request, limit=50)
|
||||
already_sorted = True
|
||||
elif show_param == "featured":
|
||||
media = Media.objects.filter(listable=True, featured=True).prefetch_related("user").order_by("-add_date")
|
||||
media = Media.objects.filter(listable=True, featured=True).prefetch_related("user", "tags")
|
||||
elif show_param == "shared_by_me":
|
||||
if not self.request.user.is_authenticated:
|
||||
media = Media.objects.none()
|
||||
else:
|
||||
media = Media.objects.filter(permissions__owner_user=self.request.user).prefetch_related("user")
|
||||
media = Media.objects.filter(permissions__owner_user=self.request.user).prefetch_related("user", "tags").distinct()
|
||||
elif show_param == "shared_with_me":
|
||||
if not self.request.user.is_authenticated:
|
||||
media = Media.objects.none()
|
||||
else:
|
||||
base_queryset = Media.objects.prefetch_related("user")
|
||||
user_media_filters = {'permissions__user': request.user}
|
||||
media = base_queryset.filter(**user_media_filters)
|
||||
base_queryset = Media.objects.prefetch_related("user", "tags")
|
||||
|
||||
# Build OR conditions similar to _get_media_queryset
|
||||
conditions = Q(permissions__user=request.user)
|
||||
|
||||
if getattr(settings, 'USE_RBAC', False):
|
||||
rbac_categories = request.user.get_rbac_categories_as_member()
|
||||
rbac_filters = {'category__in': rbac_categories}
|
||||
conditions |= Q(category__in=rbac_categories)
|
||||
|
||||
rbac_media = base_queryset.filter(**rbac_filters)
|
||||
media = media.union(rbac_media)
|
||||
media = media.order_by("-add_date")[:1000] # limit to 1000 results
|
||||
media = base_queryset.filter(conditions).distinct()
|
||||
elif author_param:
|
||||
user_queryset = User.objects.all()
|
||||
user = get_object_or_404(user_queryset, username=author_param)
|
||||
if self.request.user == user or is_mediacms_editor(self.request.user):
|
||||
media = Media.objects.filter(user=user).prefetch_related("user").order_by("-add_date")
|
||||
media = Media.objects.filter(user=user).prefetch_related("user", "tags")
|
||||
else:
|
||||
media = self._get_media_queryset(request, user)
|
||||
already_sorted = True
|
||||
|
||||
else:
|
||||
media = self._get_media_queryset(request)
|
||||
if is_mediacms_editor(self.request.user):
|
||||
media = Media.objects.prefetch_related("user", "tags")
|
||||
else:
|
||||
media = self._get_media_queryset(request)
|
||||
already_sorted = True
|
||||
|
||||
if query:
|
||||
query = helpers.clean_query(query)
|
||||
q_parts = [q_part.rstrip("y") for q_part in query.split() if q_part not in STOP_WORDS]
|
||||
if q_parts:
|
||||
query = SearchQuery(q_parts[0] + ":*", search_type="raw")
|
||||
for part in q_parts[1:]:
|
||||
query &= SearchQuery(part + ":*", search_type="raw")
|
||||
else:
|
||||
query = None
|
||||
if query:
|
||||
media = media.filter(search=query)
|
||||
|
||||
if tag:
|
||||
media = media.filter(tags__title=tag)
|
||||
|
||||
if media_type:
|
||||
media = media.filter(media_type=media_type)
|
||||
|
||||
if upload_date and gte:
|
||||
media = media.filter(add_date__gte=gte)
|
||||
|
||||
if duration:
|
||||
if duration == '0-20':
|
||||
media = media.filter(duration__gte=0, duration__lt=1200)
|
||||
elif duration == '20-40':
|
||||
media = media.filter(duration__gte=1200, duration__lt=2400)
|
||||
elif duration == '40-60':
|
||||
media = media.filter(duration__gte=2400, duration__lt=3600)
|
||||
elif duration == '60-120':
|
||||
media = media.filter(duration__gte=3600)
|
||||
|
||||
if publish_state and publish_state in ['private', 'public', 'unlisted']:
|
||||
media = media.filter(state=publish_state)
|
||||
|
||||
if not already_sorted:
|
||||
media = media.order_by(f"{ordering}{sort_by}")
|
||||
|
||||
media = media[:1000]
|
||||
|
||||
paginator = pagination_class()
|
||||
|
||||
page = paginator.paginate_queryset(media, request)
|
||||
|
||||
serializer = MediaSerializer(page, many=True, context={"request": request})
|
||||
return paginator.get_paginated_response(serializer.data)
|
||||
|
||||
tags_set = set()
|
||||
for media_obj in page:
|
||||
for tag in media_obj.tags.all():
|
||||
tags_set.add(tag.title)
|
||||
tags = ", ".join(sorted(tags_set))
|
||||
|
||||
response = paginator.get_paginated_response(serializer.data)
|
||||
response.data['tags'] = tags
|
||||
return response
|
||||
|
||||
@swagger_auto_schema(
|
||||
manual_parameters=[
|
||||
@@ -194,6 +298,16 @@ class MediaBulkUserActions(APIView):
|
||||
"set_state",
|
||||
"change_owner",
|
||||
"copy_media",
|
||||
"get_ownership",
|
||||
"set_ownership",
|
||||
"remove_ownership",
|
||||
"playlist_membership",
|
||||
"category_membership",
|
||||
"tag_membership",
|
||||
"add_to_category",
|
||||
"remove_from_category",
|
||||
"add_tags",
|
||||
"remove_tags",
|
||||
],
|
||||
),
|
||||
'playlist_ids': openapi.Schema(
|
||||
@@ -201,8 +315,28 @@ class MediaBulkUserActions(APIView):
|
||||
items=openapi.Items(type=openapi.TYPE_INTEGER),
|
||||
description="List of playlist IDs (required for add_to_playlist and remove_from_playlist actions)",
|
||||
),
|
||||
'category_uids': openapi.Schema(
|
||||
type=openapi.TYPE_ARRAY,
|
||||
items=openapi.Items(type=openapi.TYPE_STRING),
|
||||
description="List of category UIDs (required for add_to_category and remove_from_category actions)",
|
||||
),
|
||||
'tag_titles': openapi.Schema(
|
||||
type=openapi.TYPE_ARRAY,
|
||||
items=openapi.Items(type=openapi.TYPE_STRING),
|
||||
description="List of tag titles (required for add_tags and remove_tags actions)",
|
||||
),
|
||||
'state': openapi.Schema(type=openapi.TYPE_STRING, description="State to set (required for set_state action)", enum=["private", "public", "unlisted"]),
|
||||
'owner': openapi.Schema(type=openapi.TYPE_STRING, description="New owner username (required for change_owner action)"),
|
||||
'ownership_type': openapi.Schema(
|
||||
type=openapi.TYPE_STRING,
|
||||
description="Ownership type to filter/set/remove (required for get_ownership, set_ownership, and remove_ownership actions)",
|
||||
enum=["viewer", "editor", "owner"],
|
||||
),
|
||||
'users': openapi.Schema(
|
||||
type=openapi.TYPE_ARRAY,
|
||||
items=openapi.Items(type=openapi.TYPE_STRING),
|
||||
description="List of usernames (required for set_ownership and remove_ownership actions)",
|
||||
),
|
||||
},
|
||||
),
|
||||
tags=['Media'],
|
||||
@@ -215,28 +349,23 @@ class MediaBulkUserActions(APIView):
|
||||
},
|
||||
)
|
||||
def post(self, request, format=None):
|
||||
# Check if user is authenticated
|
||||
if not request.user.is_authenticated:
|
||||
return Response({"detail": "Authentication required"}, status=status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
# Get required parameters
|
||||
media_ids = request.data.get('media_ids', [])
|
||||
action = request.data.get('action')
|
||||
|
||||
# Validate required parameters
|
||||
if not media_ids:
|
||||
return Response({"detail": "media_ids is required"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
if not action:
|
||||
return Response({"detail": "action is required"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# Get media objects owned by the user
|
||||
media = Media.objects.filter(user=request.user, friendly_token__in=media_ids)
|
||||
|
||||
if not media:
|
||||
return Response({"detail": "No matching media found"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# Process based on action
|
||||
if action == "enable_comments":
|
||||
media.update(enable_comments=True)
|
||||
return Response({"detail": f"Comments enabled for {media.count()} media items"})
|
||||
@@ -307,12 +436,10 @@ class MediaBulkUserActions(APIView):
|
||||
if state not in valid_states:
|
||||
return Response({"detail": f"state must be one of {valid_states}"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# Check if user can set public state
|
||||
if not is_mediacms_editor(request.user) and settings.PORTAL_WORKFLOW != "public":
|
||||
if state == "public":
|
||||
return Response({"detail": "You are not allowed to set media to public state"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# Update media state
|
||||
for m in media:
|
||||
m.state = state
|
||||
if m.state == "public" and m.encoding_status == "success" and m.is_reviewed is True:
|
||||
@@ -343,10 +470,175 @@ class MediaBulkUserActions(APIView):
|
||||
|
||||
elif action == "copy_media":
|
||||
for m in media:
|
||||
copy_media(m.id)
|
||||
copy_media(m)
|
||||
|
||||
return Response({"detail": f"{media.count()} media items copied"})
|
||||
|
||||
elif action == "get_ownership":
|
||||
ownership_type = request.data.get('ownership_type')
|
||||
if not ownership_type:
|
||||
return Response({"detail": "ownership_type is required for get_ownership action"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
valid_ownership_types = ["viewer", "editor", "owner"]
|
||||
if ownership_type not in valid_ownership_types:
|
||||
return Response({"detail": f"ownership_type must be one of {valid_ownership_types}"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
media_count = media.count()
|
||||
|
||||
users = (
|
||||
MediaPermission.objects.filter(media__in=media, permission=ownership_type)
|
||||
.values('user__name', 'user__username')
|
||||
.annotate(media_count=Count('media', distinct=True))
|
||||
.filter(media_count=media_count)
|
||||
)
|
||||
|
||||
results = [f"{user['user__name']} - {user['user__username']}" for user in users]
|
||||
|
||||
return Response({'results': results})
|
||||
|
||||
elif action == "set_ownership":
|
||||
ownership_type = request.data.get('ownership_type')
|
||||
if not ownership_type:
|
||||
return Response({"detail": "ownership_type is required for set_ownership action"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
valid_ownership_types = ["viewer", "editor", "owner"]
|
||||
if ownership_type not in valid_ownership_types:
|
||||
return Response({"detail": f"ownership_type must be one of {valid_ownership_types}"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
usernames = request.data.get('users', [])
|
||||
if not usernames:
|
||||
return Response({"detail": "users is required for set_ownership action"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
users = User.objects.filter(username__in=usernames)
|
||||
if not users.exists():
|
||||
return Response({"detail": "No valid users found"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
for m in media:
|
||||
for user in users:
|
||||
# Create or update MediaPermission
|
||||
MediaPermission.objects.update_or_create(user=user, media=m, defaults={'owner_user': request.user, 'permission': ownership_type})
|
||||
|
||||
return Response({"detail": "Action succeeded"})
|
||||
|
||||
elif action == "remove_ownership":
|
||||
ownership_type = request.data.get('ownership_type')
|
||||
if not ownership_type:
|
||||
return Response({"detail": "ownership_type is required for remove_ownership action"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
valid_ownership_types = ["viewer", "editor", "owner"]
|
||||
if ownership_type not in valid_ownership_types:
|
||||
return Response({"detail": f"ownership_type must be one of {valid_ownership_types}"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
usernames = request.data.get('users', [])
|
||||
if not usernames:
|
||||
return Response({"detail": "users is required for remove_ownership action"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
users = User.objects.filter(username__in=usernames)
|
||||
if not users.exists():
|
||||
return Response({"detail": "No valid users found"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
MediaPermission.objects.filter(media__in=media, permission=ownership_type, user__in=users).delete()
|
||||
|
||||
return Response({"detail": "Action succeeded"})
|
||||
|
||||
elif action == "playlist_membership":
|
||||
media_count = media.count()
|
||||
|
||||
results = list(
|
||||
Playlist.objects.filter(user=request.user, playlistmedia__media__in=media)
|
||||
.values('id', 'friendly_token', 'title')
|
||||
.annotate(media_count=Count('playlistmedia__media', distinct=True))
|
||||
.filter(media_count=media_count)
|
||||
)
|
||||
|
||||
return Response({'results': results})
|
||||
|
||||
elif action == "category_membership":
|
||||
media_count = media.count()
|
||||
|
||||
results = list(Category.objects.filter(media__in=media).values('title', 'uid').annotate(media_count=Count('media', distinct=True)).filter(media_count=media_count))
|
||||
|
||||
return Response({'results': results})
|
||||
|
||||
elif action == "tag_membership":
|
||||
media_count = media.count()
|
||||
|
||||
results = list(Tag.objects.filter(media__in=media).values('title').annotate(media_count=Count('media', distinct=True)).filter(media_count=media_count))
|
||||
|
||||
return Response({'results': results})
|
||||
|
||||
elif action == "add_to_category":
|
||||
category_uids = request.data.get('category_uids', [])
|
||||
if not category_uids:
|
||||
return Response({"detail": "category_uids is required for add_to_category action"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
categories = Category.objects.filter(uid__in=category_uids)
|
||||
if not categories:
|
||||
return Response({"detail": "No matching categories found"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
added_count = 0
|
||||
for category in categories:
|
||||
for m in media:
|
||||
if not m.category.filter(uid=category.uid).exists():
|
||||
m.category.add(category)
|
||||
added_count += 1
|
||||
|
||||
return Response({"detail": f"Added {added_count} media items to {categories.count()} categories"})
|
||||
|
||||
elif action == "remove_from_category":
|
||||
category_uids = request.data.get('category_uids', [])
|
||||
if not category_uids:
|
||||
return Response({"detail": "category_uids is required for remove_from_category action"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
categories = Category.objects.filter(uid__in=category_uids)
|
||||
if not categories:
|
||||
return Response({"detail": "No matching categories found"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
removed_count = 0
|
||||
for category in categories:
|
||||
for m in media:
|
||||
if m.category.filter(uid=category.uid).exists():
|
||||
m.category.remove(category)
|
||||
removed_count += 1
|
||||
|
||||
return Response({"detail": f"Removed {removed_count} media items from {categories.count()} categories"})
|
||||
|
||||
elif action == "add_tags":
|
||||
tag_titles = request.data.get('tag_titles', [])
|
||||
if not tag_titles:
|
||||
return Response({"detail": "tag_titles is required for add_tags action"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
tags = Tag.objects.filter(title__in=tag_titles)
|
||||
if not tags:
|
||||
return Response({"detail": "No matching tags found"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
added_count = 0
|
||||
for tag in tags:
|
||||
for m in media:
|
||||
if not m.tags.filter(title=tag.title).exists():
|
||||
m.tags.add(tag)
|
||||
added_count += 1
|
||||
|
||||
return Response({"detail": f"Added {added_count} media items to {tags.count()} tags"})
|
||||
|
||||
elif action == "remove_tags":
|
||||
tag_titles = request.data.get('tag_titles', [])
|
||||
if not tag_titles:
|
||||
return Response({"detail": "tag_titles is required for remove_tags action"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
tags = Tag.objects.filter(title__in=tag_titles)
|
||||
if not tags:
|
||||
return Response({"detail": "No matching tags found"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
removed_count = 0
|
||||
for tag in tags:
|
||||
for m in media:
|
||||
if m.tags.filter(title=tag.title).exists():
|
||||
m.tags.remove(tag)
|
||||
removed_count += 1
|
||||
|
||||
return Response({"detail": f"Removed {removed_count} media items from {tags.count()} tags"})
|
||||
|
||||
else:
|
||||
return Response({"detail": f"Unknown action: {action}"}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
@@ -673,13 +965,26 @@ class MediaSearch(APIView):
|
||||
author = params.get("author", "").strip()
|
||||
upload_date = params.get('upload_date', '').strip()
|
||||
|
||||
sort_by_options = ["title", "add_date", "edit_date", "views", "likes"]
|
||||
if sort_by not in sort_by_options:
|
||||
sort_by = "add_date"
|
||||
if ordering == "asc":
|
||||
ordering = ""
|
||||
else:
|
||||
ordering = "-"
|
||||
# Handle combined sort options (e.g., title_asc, views_desc)
|
||||
parsed_combined = False
|
||||
if sort_by and '_' in sort_by:
|
||||
parts = sort_by.rsplit('_', 1)
|
||||
if len(parts) == 2 and parts[1] in ['asc', 'desc']:
|
||||
field, direction = parts
|
||||
if field in ["title", "add_date", "edit_date", "views", "likes"]:
|
||||
sort_by = field
|
||||
ordering = "" if direction == "asc" else "-"
|
||||
parsed_combined = True
|
||||
|
||||
# Fall back to legacy handling only if we didn't parse a combined option
|
||||
if not parsed_combined:
|
||||
sort_by_options = ["title", "add_date", "edit_date", "views", "likes"]
|
||||
if sort_by not in sort_by_options:
|
||||
sort_by = "add_date"
|
||||
if ordering == "asc":
|
||||
ordering = ""
|
||||
else:
|
||||
ordering = "-"
|
||||
|
||||
if media_type not in ["video", "image", "audio", "pdf"]:
|
||||
media_type = None
|
||||
@@ -689,11 +994,15 @@ class MediaSearch(APIView):
|
||||
return Response(ret, status=status.HTTP_200_OK)
|
||||
|
||||
if request.user.is_authenticated:
|
||||
basic_query = Q(listable=True) | Q(permissions__user=request.user)
|
||||
if is_mediacms_editor(self.request.user):
|
||||
media = Media.objects.prefetch_related("user", "tags")
|
||||
basic_query = Q()
|
||||
else:
|
||||
basic_query = Q(listable=True) | Q(permissions__user=request.user) | Q(user=request.user)
|
||||
|
||||
if getattr(settings, 'USE_RBAC', False):
|
||||
rbac_categories = request.user.get_rbac_categories_as_member()
|
||||
basic_query |= Q(category__in=rbac_categories)
|
||||
if getattr(settings, 'USE_RBAC', False):
|
||||
rbac_categories = request.user.get_rbac_categories_as_member()
|
||||
basic_query |= Q(category__in=rbac_categories)
|
||||
|
||||
else:
|
||||
basic_query = Q(listable=True)
|
||||
|
||||
@@ -94,7 +94,7 @@ def add_subtitle(request):
|
||||
if not media:
|
||||
return HttpResponseRedirect("/")
|
||||
|
||||
if not (request.user == media.user or is_mediacms_editor(request.user)):
|
||||
if not (is_mediacms_editor(request.user) or request.user.has_contributor_access_to_media(media)):
|
||||
return HttpResponseRedirect("/")
|
||||
|
||||
# Initialize variables
|
||||
@@ -146,7 +146,7 @@ def edit_subtitle(request):
|
||||
if not subtitle:
|
||||
return HttpResponseRedirect("/")
|
||||
|
||||
if not (request.user == subtitle.user or is_mediacms_editor(request.user)):
|
||||
if not (is_mediacms_editor(request.user) or request.user.has_contributor_access_to_media(subtitle.media)):
|
||||
return HttpResponseRedirect("/")
|
||||
|
||||
context = {"subtitle": subtitle, "action": action}
|
||||
@@ -252,7 +252,7 @@ def video_chapters(request, friendly_token):
|
||||
if not media:
|
||||
return HttpResponseRedirect("/")
|
||||
|
||||
if not (request.user == media.user or is_mediacms_editor(request.user)):
|
||||
if not (is_mediacms_editor(request.user) or request.user.has_contributor_access_to_media(media)):
|
||||
return HttpResponseRedirect("/")
|
||||
|
||||
try:
|
||||
@@ -343,6 +343,10 @@ def publish_media(request):
|
||||
if not (request.user.has_contributor_access_to_media(media) or is_mediacms_editor(request.user)):
|
||||
return HttpResponseRedirect("/")
|
||||
|
||||
if not (request.user.has_owner_access_to_media(media) or is_mediacms_editor(request.user)):
|
||||
messages.add_message(request, messages.INFO, translate_string(request.LANGUAGE_CODE, f"Permission to publish is not grated by the owner: {media.user.name}"))
|
||||
return HttpResponseRedirect(media.get_absolute_url())
|
||||
|
||||
if request.method == "POST":
|
||||
form = MediaPublishForm(request.user, request.POST, request.FILES, instance=media)
|
||||
if form.is_valid():
|
||||
@@ -370,7 +374,7 @@ def edit_chapters(request):
|
||||
if not media:
|
||||
return HttpResponseRedirect("/")
|
||||
|
||||
if not (request.user == media.user or is_mediacms_editor(request.user)):
|
||||
if not (is_mediacms_editor(request.user) or request.user.has_contributor_access_to_media(media)):
|
||||
return HttpResponseRedirect("/")
|
||||
|
||||
chapters = media.chapter_data
|
||||
@@ -395,7 +399,7 @@ def trim_video(request, friendly_token):
|
||||
if not media:
|
||||
return HttpResponseRedirect("/")
|
||||
|
||||
if not (request.user == media.user or is_mediacms_editor(request.user)):
|
||||
if not (is_mediacms_editor(request.user) or request.user.has_contributor_access_to_media(media)):
|
||||
return HttpResponseRedirect("/")
|
||||
|
||||
existing_requests = VideoTrimRequest.objects.filter(media=media, status__in=["initial", "running"]).exists()
|
||||
@@ -426,11 +430,11 @@ def edit_video(request):
|
||||
if not media:
|
||||
return HttpResponseRedirect("/")
|
||||
|
||||
if not (request.user == media.user or is_mediacms_editor(request.user)):
|
||||
if not (is_mediacms_editor(request.user) or request.user.has_contributor_access_to_media(media)):
|
||||
return HttpResponseRedirect("/")
|
||||
|
||||
if media.media_type not in ["video", "audio"]:
|
||||
messages.add_message(request, messages.INFO, "Media is not video")
|
||||
messages.add_message(request, messages.INFO, "Media is not video or audio")
|
||||
return HttpResponseRedirect(media.get_absolute_url())
|
||||
|
||||
if not settings.ALLOW_VIDEO_TRIMMER:
|
||||
@@ -629,10 +633,12 @@ def view_media(request):
|
||||
|
||||
if request.user.is_authenticated:
|
||||
if request.user.has_contributor_access_to_media(media) or is_mediacms_editor(request.user):
|
||||
context["CAN_DELETE_MEDIA"] = True
|
||||
context["CAN_EDIT_MEDIA"] = True
|
||||
context["CAN_DELETE_COMMENTS"] = True
|
||||
|
||||
if request.user == media.user or is_mediacms_editor(request.user):
|
||||
context["CAN_DELETE_MEDIA"] = True
|
||||
|
||||
# in case media is video and is processing (eg the case a video was just uploaded)
|
||||
# attempt to show it (rather than showing a blank video player)
|
||||
if media.media_type == 'video':
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from django.conf import settings
|
||||
from django.db.models import Q
|
||||
from drf_yasg import openapi
|
||||
from drf_yasg.utils import swagger_auto_schema
|
||||
from rest_framework import permissions, status
|
||||
@@ -94,7 +95,11 @@ class PlaylistDetail(APIView):
|
||||
|
||||
serializer = PlaylistDetailSerializer(playlist, context={"request": request})
|
||||
|
||||
playlist_media = PlaylistMedia.objects.filter(playlist=playlist, media__state="public").prefetch_related("media__user")
|
||||
# If user is the author, show all media; otherwise, show only public and unlisted media
|
||||
if request.user.is_authenticated and request.user == playlist.user:
|
||||
playlist_media = PlaylistMedia.objects.filter(playlist=playlist).prefetch_related("media__user").select_related("media")
|
||||
else:
|
||||
playlist_media = PlaylistMedia.objects.filter(playlist=playlist).filter(Q(media__state="public") | Q(media__state="unlisted")).prefetch_related("media__user").select_related("media")
|
||||
|
||||
playlist_media = [c.media for c in playlist_media]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user