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
=========
2.9.0
-----
- Added VideoChooserBlock
- Added video summary on admin homepage
2.4.0
-----
......
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
but for html5 videos. Includes the ability to transcode videos to a
html5 compliant codec using ffmpeg.
......@@ -31,12 +35,11 @@ Implement as a ``ForeignKey`` relation, same as wagtailimages.
.. code:: python
from django.db import models
from wagtail.wagtailadmin.edit_handlers import FieldPanel
from wagtail.wagtailcore.fields import RichTextField
from wagtail.wagtailcore.models import Page
from wagtail.admin.edit_handlers import FieldPanel
from wagtail.core.fields import RichTextField
from wagtail.core.models import Page
from wagtailvideos.edit_handlers import VideoChooserPanel
......@@ -52,6 +55,28 @@ Implement as a ``ForeignKey`` relation, same as wagtailimages.
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:
~~~~~~~~~~~~
......@@ -78,6 +103,4 @@ Future features
---------------
- Richtext embed
- Streamfield block
- Transcoding via amazon service rather than ffmpeg
- Wagtail homescreen video count
......@@ -6,3 +6,7 @@ DATABASES = {
'NAME': 'tests.sqlite3',
},
}
INSTALLED_APPS += [
'wagtail.contrib.styleguide',
]
......@@ -10,7 +10,7 @@ from setuptools import find_packages, setup # noqa: E4
setup(
name='wagtailvideos',
version='2.8.2',
version='2.9.0',
description="A wagtail module for uploading and displaying videos in various codecs.",
long_description=readme,
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 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.blocks import VideoChooserBlock
class TestPage(Page):
video_field = models.ForeignKey(
'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 + [
VideoChooserPanel('video_field')
VideoChooserPanel('video_field'),
StreamFieldPanel('video_streamfield'),
]
......@@ -50,14 +50,13 @@ MIDDLEWARE = [
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'wagtail.core.middleware.SiteMiddleware',
]
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'APP_DIRS': True,
'DIRS': ['tests/templates'],
'OPTIONS': {
'context_processors': [
'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
from django.contrib.auth import get_user_model
......@@ -9,10 +7,10 @@ from django.template.defaultfilters import filesizeformat
from django.test import TestCase, override_settings
from django.urls import reverse
from mock import patch
from tests.utils import create_test_video_file
from wagtail.core.models import Collection, GroupCollectionPermission
from wagtail.tests.utils import WagtailTestUtils
from tests.utils import create_test_video_file
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 @@
{% block titletag %}{% blocktrans with title=video.title %}Editing video {{ title }}{% endblocktrans %}{% endblock %}
{% 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>
{{ form.media.css }}
$(function() {
$('#id_tags').tagit({
autocomplete: {
......@@ -22,18 +28,17 @@
{% include "wagtailadmin/shared/header.html" with title=editing_str subtitle=video.title icon="media" %}
<div class="row row-flush nice-padding">
<div class="col5">
<form action="{% url 'wagtailvideos:edit' video.id %}" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<ul class="fields">
{% for field in form %}
{% 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 %}
{{ field }}
{% else %}
{% include "wagtailadmin/shared/field_as_li.html" %}
{% include "wagtailadmin/shared/field_as_li.html" with li_classes="label-above label-uppercase" %}
{% endif %}
{% endfor %}
<li>
......@@ -44,12 +49,12 @@
</form>
</div>
<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 %}
<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>
{% if transcodes %}
<h3 class="label">Available Transcodes</h3>
<h3 class="label no-float u-text-transform-uppercase">Available Transcodes</h3>
<ul>
{% for transcode in transcodes %}
<li>
......@@ -63,12 +68,12 @@
{% endfor %}
</ul>
{% 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">
<ul class="fields">
{% 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.quality %}
{% 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 li_classes="label-above label-uppercase" %}
<li>
<input class="button" type='submit' value="Start" />
</li>
......
......@@ -3,10 +3,16 @@ from django.urls import reverse
from django.utils.html import format_html
from django.utils.translation import ugettext_lazy as _
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 django.utils.html import format_html
from django.templatetags.static import static
from wagtailvideos import urls
from wagtailvideos.forms import GroupVideoPermissionFormSet
from wagtailvideos.models import Video
from .permissions import permission_policy
@hooks.register('register_admin_urls')
......@@ -39,3 +45,46 @@ def register_images_menu_item():
_('Videos'), reverse('wagtailvideos:index'),
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