mirror of
https://github.com/mediacms-io/mediacms.git
synced 2025-11-05 23:18:53 -05:00
Compare commits
4 Commits
43d25d6433
...
f65338562e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f65338562e | ||
|
|
7acf748c24 | ||
|
|
845ceeaed7 | ||
|
|
486c858b95 |
@ -201,7 +201,10 @@ class LanguageAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
|
|
||||||
class SubtitleAdmin(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):
|
class VideoTrimRequestAdmin(admin.ModelAdmin):
|
||||||
|
|||||||
@ -672,6 +672,9 @@ def change_media_owner(media_id, new_user):
|
|||||||
permission.owner_user = new_user
|
permission.owner_user = new_user
|
||||||
permission.save(update_fields=["owner_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
|
return media
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -42,6 +42,8 @@ class Subtitle(models.Model):
|
|||||||
user = models.ForeignKey("users.User", on_delete=models.CASCADE)
|
user = models.ForeignKey("users.User", on_delete=models.CASCADE)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
verbose_name = "Caption"
|
||||||
|
verbose_name_plural = "Captions"
|
||||||
ordering = ["language__title"]
|
ordering = ["language__title"]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|||||||
@ -94,7 +94,7 @@ def add_subtitle(request):
|
|||||||
if not media:
|
if not media:
|
||||||
return HttpResponseRedirect("/")
|
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("/")
|
return HttpResponseRedirect("/")
|
||||||
|
|
||||||
# Initialize variables
|
# Initialize variables
|
||||||
@ -146,7 +146,7 @@ def edit_subtitle(request):
|
|||||||
if not subtitle:
|
if not subtitle:
|
||||||
return HttpResponseRedirect("/")
|
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("/")
|
return HttpResponseRedirect("/")
|
||||||
|
|
||||||
context = {"subtitle": subtitle, "action": action}
|
context = {"subtitle": subtitle, "action": action}
|
||||||
@ -252,7 +252,7 @@ def video_chapters(request, friendly_token):
|
|||||||
if not media:
|
if not media:
|
||||||
return HttpResponseRedirect("/")
|
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("/")
|
return HttpResponseRedirect("/")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -370,7 +370,7 @@ def edit_chapters(request):
|
|||||||
if not media:
|
if not media:
|
||||||
return HttpResponseRedirect("/")
|
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("/")
|
return HttpResponseRedirect("/")
|
||||||
|
|
||||||
chapters = media.chapter_data
|
chapters = media.chapter_data
|
||||||
@ -395,7 +395,7 @@ def trim_video(request, friendly_token):
|
|||||||
if not media:
|
if not media:
|
||||||
return HttpResponseRedirect("/")
|
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("/")
|
return HttpResponseRedirect("/")
|
||||||
|
|
||||||
existing_requests = VideoTrimRequest.objects.filter(media=media, status__in=["initial", "running"]).exists()
|
existing_requests = VideoTrimRequest.objects.filter(media=media, status__in=["initial", "running"]).exists()
|
||||||
@ -426,7 +426,7 @@ def edit_video(request):
|
|||||||
if not media:
|
if not media:
|
||||||
return HttpResponseRedirect("/")
|
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("/")
|
return HttpResponseRedirect("/")
|
||||||
|
|
||||||
if media.media_type not in ["video", "audio"]:
|
if media.media_type not in ["video", "audio"]:
|
||||||
@ -629,10 +629,12 @@ def view_media(request):
|
|||||||
|
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated:
|
||||||
if request.user.has_contributor_access_to_media(media) or is_mediacms_editor(request.user):
|
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_EDIT_MEDIA"] = True
|
||||||
context["CAN_DELETE_COMMENTS"] = 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)
|
# 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)
|
# attempt to show it (rather than showing a blank video player)
|
||||||
if media.media_type == 'video':
|
if media.media_type == 'video':
|
||||||
|
|||||||
@ -686,9 +686,8 @@ a.item-edit-link {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show checkbox on hover or when any item is selected
|
// Show checkbox only on hover
|
||||||
&:hover .item-selection-checkbox,
|
&:hover .item-selection-checkbox {
|
||||||
&.has-any-selection .item-selection-checkbox {
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -709,18 +708,6 @@ a.item-edit-link {
|
|||||||
border-radius: 10px;
|
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
|
// Edit icon styles
|
||||||
|
|||||||
@ -24,7 +24,8 @@ export function MediaItem(props) {
|
|||||||
|
|
||||||
const finalClassname = containerClassname +
|
const finalClassname = containerClassname +
|
||||||
(props.showSelection ? ' with-selection' : '') +
|
(props.showSelection ? ' with-selection' : '') +
|
||||||
(props.isSelected ? ' selected' : '');
|
(props.isSelected ? ' selected' : '') +
|
||||||
|
(props.hasAnySelection ? ' has-any-selection' : '');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={finalClassname}>
|
<div className={finalClassname}>
|
||||||
|
|||||||
@ -70,32 +70,8 @@ export function MediaItemAudio(props) {
|
|||||||
(props.isSelected ? ' selected' : '') +
|
(props.isSelected ? ' selected' : '') +
|
||||||
(props.hasAnySelection ? ' has-any-selection' : '');
|
(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 (
|
return (
|
||||||
<div className={finalClassname} onClick={handleItemClick}>
|
<div className={finalClassname}>
|
||||||
{playlistOrderNumberComponent()}
|
{playlistOrderNumberComponent()}
|
||||||
|
|
||||||
<div className="item-content">
|
<div className="item-content">
|
||||||
|
|||||||
@ -77,32 +77,8 @@ export function MediaItemVideo(props) {
|
|||||||
(props.isSelected ? ' selected' : '') +
|
(props.isSelected ? ' selected' : '') +
|
||||||
(props.hasAnySelection ? ' has-any-selection' : '');
|
(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 (
|
return (
|
||||||
<div className={finalClassname} onClick={handleItemClick}>
|
<div className={finalClassname}>
|
||||||
{playlistOrderNumberComponent()}
|
{playlistOrderNumberComponent()}
|
||||||
|
|
||||||
<div className="item-content">
|
<div className="item-content">
|
||||||
|
|||||||
@ -217,33 +217,37 @@ export default function ViewerInfoContent(props) {
|
|||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{userCan.editMedia || userCan.deleteMedia ? (
|
{userCan.editMedia ? (
|
||||||
<div className="media-author-actions">
|
<div className="media-author-actions">
|
||||||
{userCan.editMedia ? <EditMediaButton link={MediaPageStore.get('media-data').edit_url} /> : null}
|
{userCan.editMedia ? <EditMediaButton link={MediaPageStore.get('media-data').edit_url} /> : null}
|
||||||
|
|
||||||
<PopupTrigger contentRef={popupContentRef}>
|
{userCan.deleteMedia ? (
|
||||||
<button className="remove-media-icon" title={translateString('Delete media')}>
|
<PopupTrigger contentRef={popupContentRef}>
|
||||||
<i className="material-icons">delete</i>
|
<button className="remove-media-icon" title={translateString('Delete media')}>
|
||||||
</button>
|
<i className="material-icons">delete</i>
|
||||||
</PopupTrigger>
|
</button>
|
||||||
|
</PopupTrigger>
|
||||||
|
) : null}
|
||||||
|
|
||||||
<PopupContent contentRef={popupContentRef}>
|
{userCan.deleteMedia ? (
|
||||||
<PopupMain>
|
<PopupContent contentRef={popupContentRef}>
|
||||||
<div className="popup-message">
|
<PopupMain>
|
||||||
<span className="popup-message-title">Media removal</span>
|
<div className="popup-message">
|
||||||
<span className="popup-message-main">You're willing to remove media permanently?</span>
|
<span className="popup-message-title">Media removal</span>
|
||||||
</div>
|
<span className="popup-message-main">You're willing to remove media permanently?</span>
|
||||||
<hr />
|
</div>
|
||||||
<span className="popup-message-bottom">
|
<hr />
|
||||||
<button className="button-link cancel-comment-removal" onClick={cancelMediaRemoval}>
|
<span className="popup-message-bottom">
|
||||||
CANCEL
|
<button className="button-link cancel-comment-removal" onClick={cancelMediaRemoval}>
|
||||||
</button>
|
CANCEL
|
||||||
<button className="button-link proceed-comment-removal" onClick={proceedMediaRemoval}>
|
</button>
|
||||||
PROCEED
|
<button className="button-link proceed-comment-removal" onClick={proceedMediaRemoval}>
|
||||||
</button>
|
PROCEED
|
||||||
</span>
|
</button>
|
||||||
</PopupMain>
|
</span>
|
||||||
</PopupContent>
|
</PopupMain>
|
||||||
|
</PopupContent>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</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
Loading…
x
Reference in New Issue
Block a user