Unverified Commit 6d7c3bd8 authored by seb-b's avatar seb-b Committed by GitHub
Browse files

Merge pull request #51 from neon-jungle/blocks_etc

Blocks etc
parents 92dafd28 6e853a9a
stages:
- test
- release
.python_test:
stage: test
script:
- apt-get update
- apt-get install -y ffmpeg
- python runtests.py
head:
extends: .python_test
image: python:3.8
before_script:
- pip install .['testing']
lts_27:
extends: .python_test
image: python:3.7
extends: .python_test
before_script:
- pip install .['testing'] wagtail~=2.7 django~=2.2
lts_211:
image: python:3.8
extends: .python_test
before_script:
- pip install .['testing'] wagtail~=2.11 django~=3.1
\ No newline at end of file
language: python
dist: xenial
cache:
pip: true
directories:
- $HOME/.cache/pip
- $HOME/virtualenv
env:
global:
- DJANGO_SETTINGS_MODULE="tests.app.settings"
python:
- "3.5"
- "3.6"
- "3.7"
- "3.8"
before_install:
- sudo add-apt-repository ppa:mc3man/xerus-media -y
- sudo apt-get update -qq
# The install of ffmpeg fails at random with 'There were unauthenticated packages' 🤷‍♂️
- sudo apt-get install ffmpeg --allow-unauthenticated -y
install:
- pip install --upgrade pip wheel tox-travis
script:
- tox
CHANGELOG CHANGELOG
========= =========
2.9.0
-----
- Added VideoChooserBlock
- Added video summary on admin homepage
2.4.0 2.4.0
----- -----
......
wagtailvideos wagtailvideos
============= =============
.. image:: https://gitlab.com/neonjungle/wagtailvideos/badges/master/pipeline.svg
:target: https://gitlab.com/neonjungle/wagtailvideos/pipelines?ref=master
Based on wagtailimages. The aim was to have feature parity with images Based on wagtailimages. The aim was to have feature parity with images
but for html5 videos. Includes the ability to transcode videos to a but for html5 videos. Includes the ability to transcode videos to a
html5 compliant codec using ffmpeg. html5 compliant codec using ffmpeg.
...@@ -31,12 +35,11 @@ Implement as a ``ForeignKey`` relation, same as wagtailimages. ...@@ -31,12 +35,11 @@ Implement as a ``ForeignKey`` relation, same as wagtailimages.
.. code:: python .. code:: python
from django.db import models from django.db import models
from wagtail.wagtailadmin.edit_handlers import FieldPanel from wagtail.admin.edit_handlers import FieldPanel
from wagtail.wagtailcore.fields import RichTextField from wagtail.core.fields import RichTextField
from wagtail.wagtailcore.models import Page from wagtail.core.models import Page
from wagtailvideos.edit_handlers import VideoChooserPanel from wagtailvideos.edit_handlers import VideoChooserPanel
...@@ -52,6 +55,28 @@ Implement as a ``ForeignKey`` relation, same as wagtailimages. ...@@ -52,6 +55,28 @@ Implement as a ``ForeignKey`` relation, same as wagtailimages.
VideoChooserPanel('header_video'), VideoChooserPanel('header_video'),
] ]
In a Streamfield:
~~~~~~~~~~~~~~~~~
A VideoChooserBlock is included
.. code:: python
from wagtail.admin.edit_handlers import StreamFieldPanel
from wagtail.core.fields import StreamField
from wagtail.core.models import Page
from wagtailvideos.blocks import VideoChooserBlock
class ContentPage(Page):
body = StreamField([
('video', VideoChooserBlock()),
])
content_panels = Page.content_panels + [
StreamFieldPanel('body'),
]
In template: In template:
~~~~~~~~~~~~ ~~~~~~~~~~~~
...@@ -78,6 +103,4 @@ Future features ...@@ -78,6 +103,4 @@ Future features
--------------- ---------------
- Richtext embed - Richtext embed
- Streamfield block
- Transcoding via amazon service rather than ffmpeg - Transcoding via amazon service rather than ffmpeg
- Wagtail homescreen video count
...@@ -6,3 +6,7 @@ DATABASES = { ...@@ -6,3 +6,7 @@ DATABASES = {
'NAME': 'tests.sqlite3', 'NAME': 'tests.sqlite3',
}, },
} }
INSTALLED_APPS += [
'wagtail.contrib.styleguide',
]
...@@ -10,7 +10,7 @@ from setuptools import find_packages, setup # noqa: E4 ...@@ -10,7 +10,7 @@ from setuptools import find_packages, setup # noqa: E4
setup( setup(
name='wagtailvideos', name='wagtailvideos',
version='2.8.2', version='2.9.0',
description="A wagtail module for uploading and displaying videos in various codecs.", description="A wagtail module for uploading and displaying videos in various codecs.",
long_description=readme, long_description=readme,
author='Neon Jungle', author='Neon Jungle',
......
# Generated by Django 3.1.5 on 2021-01-21 22:54
from django.db import migrations
import wagtail.core.fields
import wagtailvideos.blocks
class Migration(migrations.Migration):
dependencies = [
('app', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='testpage',
name='video_streamfield',
field=wagtail.core.fields.StreamField([('video', wagtailvideos.blocks.VideoChooserBlock())], blank=True),
),
]
from __future__ import unicode_literals
from django.db import models from django.db import models
from wagtail.core.models import Page from wagtail.core.models import Page
from wagtail.core.fields import StreamField
from wagtail.admin.edit_handlers import StreamFieldPanel
from wagtailvideos.edit_handlers import VideoChooserPanel from wagtailvideos.edit_handlers import VideoChooserPanel
from wagtailvideos.blocks import VideoChooserBlock
class TestPage(Page): class TestPage(Page):
video_field = models.ForeignKey( video_field = models.ForeignKey(
'wagtailvideos.Video', related_name='+', null=True, blank=True, on_delete=models.SET_NULL) 'wagtailvideos.Video', related_name='+', null=True, blank=True, on_delete=models.SET_NULL)
video_streamfield = StreamField([
('video', VideoChooserBlock())
], blank=True)
content_panels = Page.content_panels + [ content_panels = Page.content_panels + [
VideoChooserPanel('video_field') VideoChooserPanel('video_field'),
StreamFieldPanel('video_streamfield'),
] ]
...@@ -50,14 +50,13 @@ MIDDLEWARE = [ ...@@ -50,14 +50,13 @@ MIDDLEWARE = [
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'wagtail.core.middleware.SiteMiddleware',
] ]
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', 'BACKEND': 'django.template.backends.django.DjangoTemplates',
'APP_DIRS': True, 'APP_DIRS': True,
'DIRS': ['tests/templates'],
'OPTIONS': { 'OPTIONS': {
'context_processors': [ 'context_processors': [
'django.contrib.auth.context_processors.auth', 'django.contrib.auth.context_processors.auth',
......
from wagtail.core.models import Page, Site
from wagtail.tests.utils import WagtailPageTests
from wagtail.tests.utils.form_data import nested_form_data, streamfield
from tests.app.models import TestPage
from tests.utils import create_test_video_file
from wagtailvideos.models import Video
class TestVideoBlock(WagtailPageTests):
def setUp(self):
super().setUp()
self.root_page = Page.objects.get(pk=1)
self.video = Video.objects.create(
title="Test Video",
file=create_test_video_file()
)
def test_block_admin(self):
self.assertCanCreate(self.root_page, TestPage, nested_form_data({
'title': 'VideoPage',
'video_streamfield': streamfield([
('video', self.video.id)
])
}))
def test_block_basic_render(self):
page = self.root_page.add_child(instance=TestPage(
title='Test',
slug='vidtest',
video_streamfield=[
('video', self.video)
]
))
Site.objects.create(
hostname='localhost', port=8080, root_page=page,
site_name='Test Site', is_default_site=True
)
response = self.client.get(page.full_url)
self.assertContains(response, self.video.video_tag(attrs={"controls": True}))
{% load wagtailvideos_tags %}
<h1>{{ page.title }}</h1>
{% if self.video_field %}
{% video self.video_field controls %}
{% endif %}
{{ self.video_streamfield }}
\ No newline at end of file
from __future__ import unicode_literals
import json import json
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
...@@ -9,10 +7,10 @@ from django.template.defaultfilters import filesizeformat ...@@ -9,10 +7,10 @@ from django.template.defaultfilters import filesizeformat
from django.test import TestCase, override_settings from django.test import TestCase, override_settings
from django.urls import reverse from django.urls import reverse
from mock import patch from mock import patch
from tests.utils import create_test_video_file
from wagtail.core.models import Collection, GroupCollectionPermission from wagtail.core.models import Collection, GroupCollectionPermission
from wagtail.tests.utils import WagtailTestUtils from wagtail.tests.utils import WagtailTestUtils
from tests.utils import create_test_video_file
from wagtailvideos.models import Video from wagtailvideos.models import Video
......
[tox]
skip_missing_interpreters = True
envlist =
py{35,36,37,38}-dj{20,21}-wt24
py{35,36,37,38}-dj{20,21,22}-wt25
py{35,36,37,38}-dj{20,21,22}-wt26
py{35,36,37,38}-dj{20,21,22}-wt27
py{35,36,37,38}-dj{20,21,22}-wt28
py{36,37,38}-dj{30}-wt27
py{36,37,38}-dj{30}-wt28
# Enforce good style
flake8,isort
[travis]
python =
3.5: py35, flake8, isort
3.6: py36
3.7: py37
3.8: py38
[base]
deps = mock
[testenv]
commands = python runtests.py {posargs}
pip_pre = True
deps =
{[base]deps}
dj20: Django~=2.0.0
dj21: Django~=2.1.0
dj22: Django~=2.2.0
dj30: Django~=3.0.0
wt24: wagtail~=2.4.0
wt25: wagtail~=2.5.0
wt26: wagtail~=2.6.0
wt27: wagtail~=2.7.0
wt28: wagtail~=2.8.0
[testenv:flake8]
deps = flake8
basepython = python3.5
commands = flake8 wagtailvideos/ tests/
[testenv:isort]
usedevelop = True
deps =
{[base]deps}
isort==4.3.4
basepython = python3.5
commands = isort --recursive --diff --check-only wagtailvideos/ tests/
from wagtail.core.blocks import ChooserBlock
from django.utils.functional import cached_property
class VideoChooserBlock(ChooserBlock):
@cached_property
def target_model(self):
from wagtailvideos.models import Video
return Video
@cached_property
def widget(self):
from wagtailvideos.widgets import AdminVideoChooser
return AdminVideoChooser
def render_basic(self, value, context=None):
if value:
return value.video_tag(attrs={"controls": True})
else:
return ""
class Meta:
icon = 'media'
\ No newline at end of file
section.summary > .stats {
display: grid !important;
grid-template-columns: repeat(4, 1fr);
}
section.summary > .stats::before {
display: none !important;
}
section.summary > .stats > li {
display: flex !important;
}
@media only screen and (max-width: 50em) {
section.summary > .stats {
grid-template-columns: repeat(2, 1fr);
}
}
\ No newline at end of file
{% load i18n wagtailadmin_tags %}
<li class="icon icon-media">
<a href="{% url 'wagtailvideos:index' %}">
{% blocktrans count counter=total_videos with total_videos|intcomma as total %}
<span>{{ total }}</span> Video <span class="visuallyhidden">created in {{ site_name }}</span>
{% plural %}
<span>{{ total }}</span> Videos <span class="visuallyhidden">created in {{ site_name }}</span>
{% endblocktrans %}
</a>
</li>
\ No newline at end of file
...@@ -5,8 +5,14 @@ ...@@ -5,8 +5,14 @@
{% block titletag %}{% blocktrans with title=video.title %}Editing video {{ title }}{% endblocktrans %}{% endblock %} {% block titletag %}{% blocktrans with title=video.title %}Editing video {{ title }}{% endblocktrans %}{% endblock %}
{% block extra_css %} {% block extra_css %}
<link rel="stylesheet" href="{% static 'wagtailvideos/css/edit-video.css' %}" type="text/css" /> {% endblock %} {% block extra_js %} {{ block.super }} {% url 'wagtailadmin_tag_autocomplete' as autocomplete_url %} {{ block.super }}
<link rel="stylesheet" href="{% static 'wagtailvideos/css/edit-video.css' %}" type="text/css" /> {% endblock %}
{% block extra_js %}
{{ block.super }}
{% url 'wagtailadmin_tag_autocomplete' as autocomplete_url %}
<script> <script>
{{ form.media.css }}
$(function() { $(function() {
$('#id_tags').tagit({ $('#id_tags').tagit({
autocomplete: { autocomplete: {
...@@ -22,18 +28,17 @@ ...@@ -22,18 +28,17 @@
{% include "wagtailadmin/shared/header.html" with title=editing_str subtitle=video.title icon="media" %} {% include "wagtailadmin/shared/header.html" with title=editing_str subtitle=video.title icon="media" %}
<div class="row row-flush nice-padding"> <div class="row row-flush nice-padding">
<div class="col5"> <div class="col5">
<form action="{% url 'wagtailvideos:edit' video.id %}" method="POST" enctype="multipart/form-data"> <form action="{% url 'wagtailvideos:edit' video.id %}" method="POST" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
<ul class="fields"> <ul class="fields">
{% for field in form %} {% for field in form %}
{% if field.name == 'file' %} {% if field.name == 'file' %}
{% include "wagtailvideos/videos/_file_field_as_li.html" %} {% include "wagtailvideos/videos/_file_field_as_li.html" with li_classes="label-above label-uppercase" %}
{% elif field.is_hidden %} {% elif field.is_hidden %}
{{ field }} {{ field }}
{% else %} {% else %}
{% include "wagtailadmin/shared/field_as_li.html" %} {% include "wagtailadmin/shared/field_as_li.html" with li_classes="label-above label-uppercase" %}
{% endif %} {% endif %}
{% endfor %} {% endfor %}
<li> <li>
...@@ -44,12 +49,12 @@ ...@@ -44,12 +49,12 @@
</form> </form>
</div> </div>
<div class="col5 divider-after"> <div class="col5 divider-after">
<h2 class="label">{% trans "Video preview" %}</h2> {% video video controls style=max-width:100% %} <h2 class="label no-float u-text-transform-uppercase">{% trans "Video preview" %}</h2> {% video video controls style=max-width:100% %}
{% if can_transcode %} {% if can_transcode %}
<h3 class="label">Transcodes</h3> <h3 class="label no-float u-text-transform-uppercase">Transcodes</h3>
<p>If you wish to generate HTML5 compliant transcodes use the form below. This may take a while depending on the length of the video.</p> <p>If you wish to generate HTML5 compliant transcodes use the form below. This may take a while depending on the length of the video.</p>
{% if transcodes %} {% if transcodes %}
<h3 class="label">Available Transcodes</h3> <h3 class="label no-float u-text-transform-uppercase">Available Transcodes</h3>
<ul> <ul>
{% for transcode in transcodes %} {% for transcode in transcodes %}
<li> <li>
...@@ -63,12 +68,12 @@ ...@@ -63,12 +68,12 @@
{% endfor %} {% endfor %}
</ul> </ul>
{% endif %} {% endif %}
<h3 class="label">Create transcode</h3> <h3 class="label no-float u-text-transform-uppercase">Create transcode</h3>
<form action="{% url 'wagtailvideos:create_transcode' video.id %}" method="POST"> <form action="{% url 'wagtailvideos:create_transcode' video.id %}" method="POST">
<ul class="fields"> <ul class="fields">
{% csrf_token %} {% csrf_token %}
{% include "wagtailadmin/shared/field_as_li.html" with field=transcode_form.media_format %} {% include "wagtailadmin/shared/field_as_li.html" with field=transcode_form.media_format li_classes="label-above label-uppercase" %}
{% include "wagtailadmin/shared/field_as_li.html" with field=transcode_form.quality %} {% include "wagtailadmin/shared/field_as_li.html" with field=transcode_form.quality li_classes="label-above label-uppercase" %}
<li> <li>
<input class="button" type='submit' value="Start" /> <input class="button" type='submit' value="Start" />
</li> </li>
......
...@@ -3,10 +3,16 @@ from django.urls import reverse ...@@ -3,10 +3,16 @@ from django.urls import reverse
from django.utils.html import format_html from django.utils.html import format_html
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from wagtail.admin.menu import MenuItem from wagtail.admin.menu import MenuItem
from wagtail.admin.search import SearchArea
from wagtail.admin.site_summary import SummaryItem
from wagtail.core import hooks from wagtail.core import hooks
from django.utils.html import format_html
from django.templatetags.static import static
from wagtailvideos import urls from wagtailvideos import urls
from wagtailvideos.forms import GroupVideoPermissionFormSet from wagtailvideos.forms import GroupVideoPermissionFormSet
from wagtailvideos.models import Video
from .permissions import permission_policy
@hooks.register('register_admin_urls') @hooks.register('register_admin_urls')
...@@ -39,3 +45,46 @@ def register_images_menu_item(): ...@@ -39,3 +45,46 @@ def register_images_menu_item():
_('Videos'), reverse('wagtailvideos:index'), _('Videos'), reverse('wagtailvideos:index'),
name='videos', classnames='icon icon-media', order=300 name='videos', classnames='icon icon-media', order=300
) )
class VideoSummaryItem(SummaryItem):
order = 300
template = "wagtailvideos/homepage/videos_summary.html"
def get_context(self):
return {
"total_videos": Video.objects.count(),
}
def is_shown(self):
return permission_policy.user_has_any_permission(
self.request.user, ["add", "change", "delete"]
)
@hooks.register("construct_homepage_summary_items")
def add_media_summary_item(request, items):
items.append(VideoSummaryItem(request))
class VideoSearchArea(SearchArea):
def is_shown(self, request):
return permission_policy.user_has_any_permission(
request.user, ["add", "change", "delete"]
)
@hooks.register("register_admin_search_area")
def register_media_search_area():
return VideoSearchArea(
_("Video"),
reverse("wagtailvideos:index"),
name="video",
classnames="icon icon-media",
order=400,
)
@hooks.register('insert_global_admin_css')
def summary_css():
return format_html('<link rel="stylesheet" href="{}">', static('wagtailvideos/css/summary-override.css'))
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment