Compare commits

..

4 Commits

Author SHA1 Message Date
Markos Gogoulos
f65338562e test 2025-10-21 10:27:21 +03:00
Markos Gogoulos
7acf748c24 this 2025-10-21 09:56:47 +03:00
Markos Gogoulos
845ceeaed7 owner fix 2025-10-21 09:20:06 +03:00
Markos Gogoulos
486c858b95 fix subtitle 2025-10-21 09:05:25 +03:00
12 changed files with 54 additions and 100 deletions

View File

@ -201,7 +201,10 @@ class LanguageAdmin(admin.ModelAdmin):
class SubtitleAdmin(admin.ModelAdmin):
pass
list_display = ["id", "language", "media"]
list_filter = ["language"]
search_fields = ["media__title"]
readonly_fields = ("media", "user")
class VideoTrimRequestAdmin(admin.ModelAdmin):

View File

@ -672,6 +672,9 @@ def change_media_owner(media_id, new_user):
permission.owner_user = new_user
permission.save(update_fields=["owner_user"])
# remove any existing permissions for the new user, since they are now owner
models.MediaPermission.objects.filter(media=media, user=new_user).delete()
return media

View File

@ -42,6 +42,8 @@ class Subtitle(models.Model):
user = models.ForeignKey("users.User", on_delete=models.CASCADE)
class Meta:
verbose_name = "Caption"
verbose_name_plural = "Captions"
ordering = ["language__title"]
def __str__(self):

View File

@ -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:
@ -370,7 +370,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 +395,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,7 +426,7 @@ 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"]:
@ -629,10 +629,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':

View File

@ -686,9 +686,8 @@ a.item-edit-link {
}
}
// Show checkbox on hover or when any item is selected
&:hover .item-selection-checkbox,
&.has-any-selection .item-selection-checkbox {
// Show checkbox only on hover
&:hover .item-selection-checkbox {
opacity: 1;
}
@ -709,18 +708,6 @@ a.item-edit-link {
border-radius: 10px;
}
}
// Make the whole item clickable for selection when any selection is active
&.has-any-selection:not(.selected) {
.item-thumb {
cursor: pointer;
}
.item-main {
cursor: pointer;
pointer-events: auto;
}
}
}
// Edit icon styles

View File

@ -24,7 +24,8 @@ export function MediaItem(props) {
const finalClassname = containerClassname +
(props.showSelection ? ' with-selection' : '') +
(props.isSelected ? ' selected' : '');
(props.isSelected ? ' selected' : '') +
(props.hasAnySelection ? ' has-any-selection' : '');
return (
<div className={finalClassname}>

View File

@ -70,32 +70,8 @@ export function MediaItemAudio(props) {
(props.isSelected ? ' selected' : '') +
(props.hasAnySelection ? ' has-any-selection' : '');
const handleItemClick = (e) => {
// Only handle clicks when selection mode is active
if (props.showSelection) {
// Check if click was on the checkbox (already handled)
if (e.target.type === 'checkbox' || e.target.closest('.item-selection-checkbox')) {
return;
}
// Check if click was on the edit icon or view icon
if (e.target.closest('.item-edit-icon') || e.target.closest('.item-view-icon')) {
return;
}
// Prevent default link behavior
e.preventDefault();
e.stopPropagation();
// Toggle the checkbox
if (props.onCheckboxChange) {
props.onCheckboxChange({ target: { checked: !props.isSelected } });
}
}
};
return (
<div className={finalClassname} onClick={handleItemClick}>
<div className={finalClassname}>
{playlistOrderNumberComponent()}
<div className="item-content">

View File

@ -77,32 +77,8 @@ export function MediaItemVideo(props) {
(props.isSelected ? ' selected' : '') +
(props.hasAnySelection ? ' has-any-selection' : '');
const handleItemClick = (e) => {
// Only handle clicks when selection mode is active
if (props.showSelection) {
// Check if click was on the checkbox (already handled)
if (e.target.type === 'checkbox' || e.target.closest('.item-selection-checkbox')) {
return;
}
// Check if click was on the edit icon or view icon
if (e.target.closest('.item-edit-icon') || e.target.closest('.item-view-icon')) {
return;
}
// Prevent default link behavior
e.preventDefault();
e.stopPropagation();
// Toggle the checkbox
if (props.onCheckboxChange) {
props.onCheckboxChange({ target: { checked: !props.isSelected } });
}
}
};
return (
<div className={finalClassname} onClick={handleItemClick}>
<div className={finalClassname}>
{playlistOrderNumberComponent()}
<div className="item-content">

View File

@ -217,33 +217,37 @@ export default function ViewerInfoContent(props) {
/>
) : null}
{userCan.editMedia || userCan.deleteMedia ? (
{userCan.editMedia ? (
<div className="media-author-actions">
{userCan.editMedia ? <EditMediaButton link={MediaPageStore.get('media-data').edit_url} /> : null}
<PopupTrigger contentRef={popupContentRef}>
<button className="remove-media-icon" title={translateString('Delete media')}>
<i className="material-icons">delete</i>
</button>
</PopupTrigger>
{userCan.deleteMedia ? (
<PopupTrigger contentRef={popupContentRef}>
<button className="remove-media-icon" title={translateString('Delete media')}>
<i className="material-icons">delete</i>
</button>
</PopupTrigger>
) : null}
<PopupContent contentRef={popupContentRef}>
<PopupMain>
<div className="popup-message">
<span className="popup-message-title">Media removal</span>
<span className="popup-message-main">You're willing to remove media permanently?</span>
</div>
<hr />
<span className="popup-message-bottom">
<button className="button-link cancel-comment-removal" onClick={cancelMediaRemoval}>
CANCEL
</button>
<button className="button-link proceed-comment-removal" onClick={proceedMediaRemoval}>
PROCEED
</button>
</span>
</PopupMain>
</PopupContent>
{userCan.deleteMedia ? (
<PopupContent contentRef={popupContentRef}>
<PopupMain>
<div className="popup-message">
<span className="popup-message-title">Media removal</span>
<span className="popup-message-main">You're willing to remove media permanently?</span>
</div>
<hr />
<span className="popup-message-bottom">
<button className="button-link cancel-comment-removal" onClick={cancelMediaRemoval}>
CANCEL
</button>
<button className="button-link proceed-comment-removal" onClick={proceedMediaRemoval}>
PROCEED
</button>
</span>
</PopupMain>
</PopupContent>
) : null}
</div>
) : null}
</div>

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