Skip to content

Commit

Permalink
[#1714] Created CMS Videoplayer plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
Bart van der Schoor committed Sep 19, 2023
1 parent c294eef commit bb33f64
Show file tree
Hide file tree
Showing 27 changed files with 435 additions and 0 deletions.
Empty file.
1 change: 1 addition & 0 deletions src/open_inwoner/cms/plugins/cms_plugins/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .videoplayer import VideoPlayerPlugin
18 changes: 18 additions & 0 deletions src/open_inwoner/cms/plugins/cms_plugins/videoplayer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from django.utils.translation import gettext as _

from cms.plugin_base import CMSPluginBase
from cms.plugin_pool import plugin_pool

from open_inwoner.cms.plugins.models.videoplayer import VideoPlayer


@plugin_pool.register_plugin
class VideoPlayerPlugin(CMSPluginBase):
model = VideoPlayer
module = _("Media")
name = _("Video Player")
render_template = "cms/plugins/videoplayer/videoplayer.html"

def render(self, context, instance, placeholder):
context.update({"instance": instance})
return context
46 changes: 46 additions & 0 deletions src/open_inwoner/cms/plugins/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Generated by Django 3.2.20 on 2023-09-19 13:04

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
("media", "0001_initial"),
("cms", "0022_auto_20180620_1551"),
]

operations = [
migrations.CreateModel(
name="VideoPlayer",
fields=[
(
"cmsplugin_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
related_name="plugins_videoplayer",
serialize=False,
to="cms.cmsplugin",
),
),
(
"video",
models.ForeignKey(
help_text="The video from the catalog.",
on_delete=django.db.models.deletion.PROTECT,
to="media.video",
),
),
],
options={
"abstract": False,
},
bases=("cms.cmsplugin",),
),
]
Empty file.
1 change: 1 addition & 0 deletions src/open_inwoner/cms/plugins/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .videoplayer import VideoPlayer
20 changes: 20 additions & 0 deletions src/open_inwoner/cms/plugins/models/videoplayer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from django.db import models
from django.utils.translation import gettext_lazy as _

from cms.models import CMSPlugin

from open_inwoner.media.models import Video


class VideoPlayer(CMSPlugin):
video = models.ForeignKey(
Video,
help_text=_("The video from the catalog."),
on_delete=models.PROTECT,
)

def __str__(self):
if self.video_id:
return str(self.video)
else:
return super().__str__()
Empty file.
16 changes: 16 additions & 0 deletions src/open_inwoner/cms/plugins/tests/test_videoplayer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from django.test import TestCase

from open_inwoner.cms.tests import cms_tools
from open_inwoner.media.tests.factories import VideoFactory

from ..cms_plugins import VideoPlayerPlugin


class TestVideoPlayerPlugin(TestCase):
def test_plugin(self):
video = VideoFactory()
html, context = cms_tools.render_plugin(
VideoPlayerPlugin, plugin_data={"video": video}
)
self.assertIn(video.player_url, html)
self.assertIn("<iframe ", html)
3 changes: 3 additions & 0 deletions src/open_inwoner/conf/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@
"open_inwoner.questionnaire",
"open_inwoner.extended_sessions",
"open_inwoner.custom_csp",
"open_inwoner.media",
"open_inwoner.cms.profile",
"open_inwoner.cms.cases",
"open_inwoner.cms.inbox",
Expand All @@ -215,6 +216,7 @@
"open_inwoner.cms.banner",
"open_inwoner.cms.extensions",
"open_inwoner.cms.footer",
"open_inwoner.cms.plugins",
]

MIDDLEWARE = [
Expand Down Expand Up @@ -531,6 +533,7 @@
"plugins": [
# "TextPlugin",
"PicturePlugin",
"VideoPlayerPlugin",
"CategoriesPlugin",
"ActivePlansPlugin",
"QuestionnairePlugin",
Expand Down
Empty file.
1 change: 1 addition & 0 deletions src/open_inwoner/media/admin/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .video import VideoAdmin
46 changes: 46 additions & 0 deletions src/open_inwoner/media/admin/video.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from django.contrib import admin
from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _

from ..models import Video


@admin.register(Video)
class VideoAdmin(admin.ModelAdmin):
fields = [
"link_id",
"title",
"player_type",
"language",
"external_url_link",
]
list_display_links = [
"link_id",
"title",
]
list_display = list_display_links + [
"player_type",
"language",
"external_url_link",
]
search_fields = [
"link_id",
"title",
]
list_filter = [
"player_type",
]
readonly_fields = [
"external_url_link",
]

@admin.display(description=_("External URL"), ordering=("player_type", "link_id"))
def external_url_link(self, video):
if not video.link_id:
return "-"
url = video.external_url
return format_html(
'<a href="{url}" rel="noopener" target="_blank">{text}</a>',
url=url,
text=url,
)
8 changes: 8 additions & 0 deletions src/open_inwoner/media/choices.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.utils.translation import ugettext_lazy as _

from djchoices import ChoiceItem, DjangoChoices


class VideoPlayerChoices(DjangoChoices):
vimeo = ChoiceItem("vimeo", _("Vimeo"))
youtube = ChoiceItem("youtube", _("Youtube"))
63 changes: 63 additions & 0 deletions src/open_inwoner/media/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Generated by Django 3.2.20 on 2023-09-19 11:53

from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = []

operations = [
migrations.CreateModel(
name="Video",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"link_id",
models.CharField(
help_text="https://vimeo.com/[Video ID] | https://www.youtube.com/watch?v=[Video ID]",
max_length=100,
verbose_name="video ID",
),
),
(
"player_type",
models.CharField(
choices=[("vimeo", "Vimeo"), ("youtube", "Youtube")],
default="vimeo",
max_length=200,
verbose_name="Player type",
),
),
(
"title",
models.CharField(
blank=True, default="", max_length=200, verbose_name="title"
),
),
(
"language",
models.CharField(
choices=[("nl", "Dutch")],
default="nl",
max_length=20,
verbose_name="language",
),
),
],
options={
"verbose_name": "Video",
"ordering": ("title",),
},
),
]
Empty file.
1 change: 1 addition & 0 deletions src/open_inwoner/media/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .video import Video # noqa
82 changes: 82 additions & 0 deletions src/open_inwoner/media/models/video.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
from django.conf import settings
from django.db import models
from django.utils.translation import ugettext_lazy as _

from open_inwoner.media.choices import VideoPlayerChoices
from open_inwoner.utils.text import middle_truncate


class VideoManager(models.Manager):
pass


class Video(models.Model):
link_id = models.CharField(
_("video ID"),
max_length=100,
help_text=_(
"https://vimeo.com/[Video ID] | https://www.youtube.com/watch?v=[Video ID]"
),
)
player_type = models.CharField(
_("Player type"),
max_length=200,
default=VideoPlayerChoices.vimeo,
choices=VideoPlayerChoices.choices,
)
title = models.CharField(
_("title"),
max_length=200,
default="",
blank=True,
)
language = models.CharField(
_("language"),
max_length=20,
choices=settings.LANGUAGES,
default=settings.LANGUAGE_CODE,
)

objects = VideoManager()

class Meta:
verbose_name = _("Video")
ordering = ("title",)

def __str__(self):
if not self.title:
return self.link_id

return "{} ({}: {}, {})".format(
middle_truncate(self.title, 50),
self.player_type,
self.link_id,
self.language,
)

@property
def external_url(self):
if self.player_type == VideoPlayerChoices.youtube:
url = "https://www.youtube.com/watch?v={link_id}&enablejsapi=1"
elif self.player_type == VideoPlayerChoices.vimeo:
url = "https://vimeo.com/{link_id}"
else:
raise Exception("unsupported player_type")
return url.format(link_id=self.link_id)

@property
def player_url(self):
if self.player_type == VideoPlayerChoices.youtube:
separator = "?"
if "?" in self.link_id:
separator = "&"
url = (
"https://www.youtube.com/embed/{link_id}"
+ separator
+ "enablejsapi=1&modestbranding=1"
)
elif self.player_type == VideoPlayerChoices.vimeo:
url = "https://player.vimeo.com/video/{link_id}"
else:
raise Exception("unsupported player_type")
return url.format(link_id=self.link_id)
Empty file.
12 changes: 12 additions & 0 deletions src/open_inwoner/media/tests/factories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import factory

from open_inwoner.media.choices import VideoPlayerChoices


class VideoFactory(factory.django.DjangoModelFactory):
link_id = factory.Faker("ean13")
title = factory.Faker("sentence")
player_type = VideoPlayerChoices.vimeo

class Meta:
model = "media.Video"
40 changes: 40 additions & 0 deletions src/open_inwoner/media/tests/test_admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from django.urls import reverse

from cms.utils.permissions import set_current_user
from django_webtest import WebTest

from open_inwoner.accounts.tests.factories import UserFactory

from .factories import VideoFactory


class VideoAdminTests(WebTest):
def setUp(self):
set_current_user(
None
) # otherwise will assume previous user is logged in (who is often deleted after test)
return super().setUp()

def test_get_changelist(self):
user = UserFactory(is_staff=True, is_superuser=True)
url = reverse("admin:media_video_changelist")
response = self.app.get(url, user=user)
self.assertEqual(response.status_code, 200)

def test_get_changelist_with_videos(self):
video = VideoFactory()
user = UserFactory(is_staff=True, is_superuser=True)
url = reverse("admin:media_video_changelist")
response = self.app.get(url, user=user)
self.assertEqual(response.status_code, 200)
self.assertContains(response, video.link_id)
self.assertContains(response, video.external_url)

def test_get_change(self):
video = VideoFactory()
user = UserFactory(is_staff=True, is_superuser=True)
url = reverse("admin:media_video_change", kwargs={"object_id": video.id})
response = self.app.get(url, user=user)
self.assertEqual(response.status_code, 200)
self.assertContains(response, video.link_id)
self.assertContains(response, video.external_url)
Loading

0 comments on commit bb33f64

Please sign in to comment.