mirror of
https://github.com/mediacms-io/mediacms.git
synced 2025-11-20 05:36:03 -05:00
feat: update versions for python packages, add Pages functionality (#1386)
This PR updates Django core version and also brings html pages support (that admins can create)
This commit is contained in:
@@ -3,6 +3,7 @@ from django.conf import settings
|
||||
from django.contrib import admin
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import transaction
|
||||
from tinymce.widgets import TinyMCE
|
||||
|
||||
from rbac.models import RBACGroup
|
||||
|
||||
@@ -13,8 +14,10 @@ from .models import (
|
||||
Encoding,
|
||||
Language,
|
||||
Media,
|
||||
Page,
|
||||
Subtitle,
|
||||
Tag,
|
||||
TinyMCEMedia,
|
||||
TranscriptionRequest,
|
||||
VideoTrimRequest,
|
||||
)
|
||||
@@ -224,11 +227,39 @@ class TranscriptionRequestAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class PageAdminForm(forms.ModelForm):
|
||||
description = forms.CharField(widget=TinyMCE())
|
||||
|
||||
def clean_description(self):
|
||||
content = self.cleaned_data['description']
|
||||
# Add sandbox attribute to all iframes
|
||||
content = content.replace('<iframe ', '<iframe sandbox="allow-scripts allow-same-origin allow-presentation" ')
|
||||
return content
|
||||
|
||||
class Meta:
|
||||
model = Page
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
class PageAdmin(admin.ModelAdmin):
|
||||
form = PageAdminForm
|
||||
|
||||
|
||||
@admin.register(TinyMCEMedia)
|
||||
class TinyMCEMediaAdmin(admin.ModelAdmin):
|
||||
list_display = ['original_filename', 'file_type', 'uploaded_at', 'user']
|
||||
list_filter = ['file_type', 'uploaded_at']
|
||||
search_fields = ['original_filename']
|
||||
readonly_fields = ['uploaded_at']
|
||||
date_hierarchy = 'uploaded_at'
|
||||
|
||||
|
||||
admin.site.register(EncodeProfile, EncodeProfileAdmin)
|
||||
admin.site.register(Comment, CommentAdmin)
|
||||
admin.site.register(Media, MediaAdmin)
|
||||
admin.site.register(Encoding, EncodingAdmin)
|
||||
admin.site.register(Category, CategoryAdmin)
|
||||
admin.site.register(Page, PageAdmin)
|
||||
admin.site.register(Tag, TagAdmin)
|
||||
admin.site.register(Subtitle, SubtitleAdmin)
|
||||
admin.site.register(Language, LanguageAdmin)
|
||||
|
||||
@@ -429,6 +429,9 @@ def user_allowed_to_upload(request):
|
||||
|
||||
def can_transcribe_video(user):
|
||||
"""Checks if a user can transcribe a video."""
|
||||
if not getattr(settings, 'USE_WHISPER_TRANSCRIBE', False):
|
||||
return False
|
||||
|
||||
if is_mediacms_editor(user):
|
||||
return True
|
||||
if getattr(settings, 'USER_CAN_TRANSCRIBE_VIDEO', False):
|
||||
|
||||
42
files/migrations/0013_page_tinymcemedia.py
Normal file
42
files/migrations/0013_page_tinymcemedia.py
Normal file
@@ -0,0 +1,42 @@
|
||||
# Generated by Django 5.2.6 on 2025-09-21 11:49
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('files', '0012_media_allow_whisper_transcribe_and_more'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Page',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('slug', models.SlugField(max_length=200, unique=True)),
|
||||
('title', models.CharField(max_length=200)),
|
||||
('description', models.TextField(blank=True)),
|
||||
('add_date', models.DateTimeField(auto_now_add=True)),
|
||||
('edit_date', models.DateTimeField(auto_now=True)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TinyMCEMedia',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('file', models.FileField(upload_to='tinymce_media/')),
|
||||
('uploaded_at', models.DateTimeField(auto_now_add=True)),
|
||||
('file_type', models.CharField(choices=[('image', 'Image'), ('media', 'Media')], max_length=10)),
|
||||
('original_filename', models.CharField(max_length=255)),
|
||||
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'TinyMCE Media',
|
||||
'verbose_name_plural': 'TinyMCE Media',
|
||||
'ordering': ['-uploaded_at'],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -4,6 +4,7 @@ from .comment import Comment # noqa: F401
|
||||
from .encoding import EncodeProfile, Encoding # noqa: F401
|
||||
from .license import License # noqa: F401
|
||||
from .media import Media, MediaPermission # noqa: F401
|
||||
from .page import Page, TinyMCEMedia # noqa: F401
|
||||
from .playlist import Playlist, PlaylistMedia # noqa: F401
|
||||
from .rating import Rating, RatingCategory # noqa: F401
|
||||
from .subtitle import Language, Subtitle, TranscriptionRequest # noqa: F401
|
||||
|
||||
42
files/models/page.py
Normal file
42
files/models/page.py
Normal file
@@ -0,0 +1,42 @@
|
||||
from django.db import models
|
||||
from django.urls import reverse
|
||||
|
||||
|
||||
class Page(models.Model):
|
||||
slug = models.SlugField(max_length=200, unique=True)
|
||||
title = models.CharField(max_length=200)
|
||||
description = models.TextField(blank=True)
|
||||
add_date = models.DateTimeField(auto_now_add=True)
|
||||
edit_date = models.DateTimeField(auto_now=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse("get_page", args=[str(self.slug)])
|
||||
|
||||
|
||||
class TinyMCEMedia(models.Model):
|
||||
file = models.FileField(upload_to='tinymce_media/')
|
||||
uploaded_at = models.DateTimeField(auto_now_add=True)
|
||||
file_type = models.CharField(
|
||||
max_length=10,
|
||||
choices=(
|
||||
('image', 'Image'),
|
||||
('media', 'Media'),
|
||||
),
|
||||
)
|
||||
original_filename = models.CharField(max_length=255)
|
||||
user = models.ForeignKey("users.User", on_delete=models.CASCADE, null=True, blank=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'TinyMCE Media'
|
||||
verbose_name_plural = 'TinyMCE Media'
|
||||
ordering = ['-uploaded_at']
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.original_filename} ({self.file_type})"
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
return self.file.url
|
||||
20
files/tinymce_handlers.py
Normal file
20
files/tinymce_handlers.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from django.http import JsonResponse
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
|
||||
from .models import TinyMCEMedia
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
def upload_image(request):
|
||||
if not (request.user.is_authenticated and request.user.is_active and request.user.is_staff and request.user.is_superuser):
|
||||
return JsonResponse({'error': 'Admin access required'}, status=403)
|
||||
|
||||
if request.method == "POST":
|
||||
file_obj = request.FILES.get('file')
|
||||
if file_obj:
|
||||
# Create a new TinyMCEMedia instance for the image
|
||||
media = TinyMCEMedia(file=file_obj, file_type='image', original_filename=file_obj.name, user=request.user if request.user.is_authenticated else None)
|
||||
media.save()
|
||||
|
||||
return JsonResponse({'location': media.url})
|
||||
return JsonResponse({'error': 'Invalid request'}, status=400)
|
||||
@@ -4,7 +4,7 @@ from django.conf.urls import include
|
||||
from django.conf.urls.static import static
|
||||
from django.urls import path, re_path
|
||||
|
||||
from . import management_views, views
|
||||
from . import management_views, tinymce_handlers, views
|
||||
from .feeds import IndexRSSFeed, SearchRSSFeed
|
||||
|
||||
friendly_token = r"(?P<friendly_token>[\w\-_]*)"
|
||||
@@ -12,7 +12,6 @@ friendly_token = r"(?P<friendly_token>[\w\-_]*)"
|
||||
urlpatterns = [
|
||||
path("i18n/", include("django.conf.urls.i18n")),
|
||||
re_path(r"^$", views.index),
|
||||
re_path(r"^about", views.about, name="about"),
|
||||
re_path(r"^setlanguage", views.setlanguage, name="setlanguage"),
|
||||
re_path(r"^add_subtitle", views.add_subtitle, name="add_subtitle"),
|
||||
re_path(r"^edit_subtitle", views.edit_subtitle, name="edit_subtitle"),
|
||||
@@ -108,8 +107,13 @@ urlpatterns = [
|
||||
re_path(r"^manage/comments$", views.manage_comments, name="manage_comments"),
|
||||
re_path(r"^manage/media$", views.manage_media, name="manage_media"),
|
||||
re_path(r"^manage/users$", views.manage_users, name="manage_users"),
|
||||
# Media uploads in ADMIN created pages
|
||||
re_path(r"^tinymce/upload/", tinymce_handlers.upload_image, name="tinymce_upload_image"),
|
||||
re_path("^(?P<slug>[\w.-]*)$", views.get_page, name="get_page"), # noqa: W605
|
||||
re_path(r"^about", views.about, name="about"),
|
||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
||||
|
||||
if settings.USERS_NEEDS_TO_BE_APPROVED:
|
||||
urlpatterns.append(re_path(r"^approval_required", views.approval_required, name="approval_required"))
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ from .pages import edit_subtitle # noqa: F401
|
||||
from .pages import edit_video # noqa: F401
|
||||
from .pages import embed_media # noqa: F401
|
||||
from .pages import featured_media # noqa: F401
|
||||
from .pages import get_page # noqa: F401
|
||||
from .pages import history # noqa: F401
|
||||
from .pages import index # noqa: F401
|
||||
from .pages import latest_media # noqa: F401
|
||||
|
||||
@@ -31,10 +31,20 @@ from ..methods import (
|
||||
is_media_allowed_type,
|
||||
is_mediacms_editor,
|
||||
)
|
||||
from ..models import Category, Media, Playlist, Subtitle, Tag, VideoTrimRequest
|
||||
from ..models import Category, Media, Page, Playlist, Subtitle, Tag, VideoTrimRequest
|
||||
from ..tasks import save_user_action, video_trim_task
|
||||
|
||||
|
||||
def get_page(request, slug):
|
||||
context = {}
|
||||
page = Page.objects.filter(slug=slug).first()
|
||||
if page:
|
||||
context["page"] = page
|
||||
else:
|
||||
return render(request, "404.html", context)
|
||||
return render(request, "cms/page.html", context)
|
||||
|
||||
|
||||
@login_required
|
||||
def record_screen(request):
|
||||
"""Record screen view"""
|
||||
|
||||
Reference in New Issue
Block a user