Add video block to frontend and backend

This commit is contained in:
Ramon Wenger 2018-09-12 10:27:40 +02:00
parent 4cb5c6ad63
commit 4010b21cce
11 changed files with 147 additions and 36 deletions

View File

@ -23,11 +23,12 @@
</template> </template>
<script> <script>
import TextBlock from '@/components/content-blocks/TextBlock.vue'; import TextBlock from '@/components/content-blocks/TextBlock';
import BasicKnowledgeWidget from '@/components/content-blocks/BasicKnowledgeWidget.vue'; import BasicKnowledgeWidget from '@/components/content-blocks/BasicKnowledgeWidget';
import Task from '@/components/content-blocks/Task.vue'; import Task from '@/components/content-blocks/Task';
import ImageBlock from '@/components/content-blocks/ImageBlock.vue'; import ImageBlock from '@/components/content-blocks/ImageBlock';
import StudentEntry from '@/components/content-blocks/StudentEntry.vue'; import VideoBlock from '@/components/content-blocks/VideoBlock';
import StudentEntry from '@/components/content-blocks/StudentEntry';
import AddContentBlockButton from '@/components/AddContentBlockButton'; import AddContentBlockButton from '@/components/AddContentBlockButton';
import EyeIcon from '@/components/icons/EyeIcon'; import EyeIcon from '@/components/icons/EyeIcon';
@ -39,6 +40,7 @@
'basic_knowledge': BasicKnowledgeWidget, 'basic_knowledge': BasicKnowledgeWidget,
'student_entry': StudentEntry, 'student_entry': StudentEntry,
'image_block': ImageBlock, 'image_block': ImageBlock,
'video_block': VideoBlock,
Task, Task,
AddContentBlockButton, AddContentBlockButton,
EyeIcon EyeIcon

View File

@ -33,7 +33,7 @@
<script> <script>
import Modal from '@/components/Modal'; import Modal from '@/components/Modal';
import ContentBlockChooserWidget from '@/components/ContentBlockChooserWidget'; import ContentBlockChooserWidget from '@/components/content-forms/ContentBlockChooserWidget';
import ContentBlockTitleInput from '@/components/ContentBlockTitleInput'; import ContentBlockTitleInput from '@/components/ContentBlockTitleInput';
import AddContentElement from '@/components/AddContentElement'; import AddContentElement from '@/components/AddContentElement';
import LinkForm from '@/components/content-forms/LinkForm'; import LinkForm from '@/components/content-forms/LinkForm';
@ -66,7 +66,7 @@
switch (element.type) { switch (element.type) {
case 'link': case 'link':
return 'link-form'; return 'link-form';
case 'video': case 'video_block':
return 'video-form'; return 'video-form';
case 'image': case 'image':
return 'image-form'; return 'image-form';
@ -124,6 +124,12 @@
url: '' url: ''
}; };
break; break;
case 'video_block':
el = {
...el,
url: ''
};
break;
} }
this.elements.splice(index, 1, el); this.elements.splice(index, 1, el);

View File

@ -0,0 +1,36 @@
<template>
<div class="video-block">
<youtube-embed v-if="isYoutube" :url="value.url"></youtube-embed>
<vimeo-embed v-if="isVimeo" :url="value.url"></vimeo-embed>
</div>
</template>
<script>
import YoutubeEmbed from '@/components/videos/YoutubeEmbed';
import VimeoEmbed from '@/components/videos/VimeoEmbed';
import {isVimeoUrl, isYoutubeUrl} from '@/helpers/video';
export default {
props: ['value'],
components: {
YoutubeEmbed,
VimeoEmbed
},
computed: {
isYoutube() {
return isYoutubeUrl(this.value.url);
},
isVimeo() {
return isVimeoUrl(this.value.url);
}
}
}
</script>
<style scoped lang="scss">
.video-block {
margin-bottom: 30px;
}
</style>

View File

@ -4,7 +4,7 @@
<link-icon class="content-block-chooser-widget__link-icon"></link-icon> <link-icon class="content-block-chooser-widget__link-icon"></link-icon>
<div class="content-block-chooser-widget__link-title">Link</div> <div class="content-block-chooser-widget__link-title">Link</div>
</div> </div>
<div class="content-block-chooser-widget__link" v-on:click="$emit('change-type', index, 'video')"> <div class="content-block-chooser-widget__link" v-on:click="$emit('change-type', index, 'video_block')">
<video-icon class="content-block-chooser-widget__link-icon"></video-icon> <video-icon class="content-block-chooser-widget__link-icon"></video-icon>
<div class="content-block-chooser-widget__link-title">Video</div> <div class="content-block-chooser-widget__link-title">Video</div>
</div> </div>

View File

@ -15,14 +15,10 @@
</div> </div>
<div v-if="isYoutube"> <div v-if="isYoutube">
<iframe id="ytplayer" type="text/html" <youtube-embed :url="url"></youtube-embed>
rel="0"
:src="`https://www.youtube.com/embed/${videoId}`"
frameborder="0"></iframe>
</div> </div>
<div v-if="isVimeo"> <div v-if="isVimeo">
<iframe :src="`https://player.vimeo.com/video/${videoId}`" frameborder="0" <vimeo-embed :url="url"></vimeo-embed>
webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div> </div>
</div> </div>
@ -30,32 +26,25 @@
<script> <script>
import InfoIcon from '@/components/icons/InfoIcon'; import InfoIcon from '@/components/icons/InfoIcon';
import YoutubeEmbed from '@/components/videos/YoutubeEmbed';
const YOUTUBE = /^(?:https:\/\/)?(?:www.)?youtube.com\/watch\?v=([a-zA-Z0-9]*)$/; import VimeoEmbed from '@/components/videos/VimeoEmbed';
const VIMEO = /^(?:https:\/\/)?(?:www.)?vimeo.com\/([a-zA-Z0-9]*)$/; import {isVimeoUrl, isYoutubeUrl} from '@/helpers/video';
export default { export default {
props: ['url', 'index'], props: ['url', 'index'],
components: { components: {
InfoIcon InfoIcon,
YoutubeEmbed,
VimeoEmbed
}, },
computed: { computed: {
isYoutube() { isYoutube() {
return YOUTUBE.test(this.url); return isYoutubeUrl(this.url);
}, },
isVimeo() { isVimeo() {
return VIMEO.test(this.url); return isVimeoUrl(this.url);
},
videoId() {
if (this.isYoutube) {
return YOUTUBE.exec(this.url)[1];
} else if (this.isVimeo) {
return VIMEO.exec(this.url)[1];
} else {
return '';
}
} }
} }
} }
@ -95,9 +84,5 @@
} }
iframe {
width: 100%;
height: 350px;
}
</style> </style>

View File

@ -0,0 +1,25 @@
<template>
<iframe class="vimeo-embed" :src="`https://player.vimeo.com/video/${videoId}`" frameborder="0"
webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</template>
<script>
import {getVideoId} from '@/helpers/video';
export default {
props: ['url'],
computed: {
videoId() {
return getVideoId(this.url);
}
}
}
</script>
<style scoped lang="scss">
.vimeo-embed {
width: 100%;
height: 350px;
}
</style>

View File

@ -0,0 +1,27 @@
<template>
<iframe class="youtube-embed" id="ytplayer" type="text/html"
rel="0"
:src="`https://www.youtube.com/embed/${videoId}`"
frameborder="0"></iframe>
</template>
<script>
import {getVideoId} from '@/helpers/video';
export default {
props: ['url'],
computed: {
videoId() {
return getVideoId(this.url);
}
}
}
</script>
<style scoped lang="scss">
.youtube-embed {
width: 100%;
height: 350px;
}
</style>

View File

@ -0,0 +1,20 @@
const YOUTUBE = /^(?:https:\/\/)?(?:www.)?youtube.com\/watch\?v=([a-zA-Z0-9_-]{11})$/;
const VIMEO = /^(?:https:\/\/)?(?:www.)?vimeo.com\/([a-zA-Z0-9]*)$/;
export function isYoutubeUrl(url) {
return YOUTUBE.test(url);
}
export function isVimeoUrl(url) {
return VIMEO.test(url);
}
export function getVideoId(url) {
if (isYoutubeUrl(url)) {
return YOUTUBE.exec(url)[1];
} else if (isVimeoUrl(url)) {
return VIMEO.exec(url)[1];
} else {
return '';
}
}

View File

@ -31,6 +31,11 @@ class StudentEntryBlock(blocks.StructBlock):
task_text = blocks.RichTextBlock() task_text = blocks.RichTextBlock()
# 'video_block'
class VideoBlock(blocks.StructBlock):
url = blocks.URLBlock()
# 'text_block' 'task' 'basic_knowledge' 'student_entry' 'image_block' # 'text_block' 'task' 'basic_knowledge' 'student_entry' 'image_block'
# #
# url = blocks.URLBlock() # url = blocks.URLBlock()

View File

@ -5,7 +5,7 @@ from wagtail.admin.edit_handlers import FieldPanel, TabbedInterface, ObjectList,
from wagtail.core.fields import StreamField from wagtail.core.fields import StreamField
from wagtail.images.blocks import ImageChooserBlock from wagtail.images.blocks import ImageChooserBlock
from book.blocks import TextBlock, BasicKnowledgeBlock, StudentEntryBlock, LinkBlock from book.blocks import TextBlock, BasicKnowledgeBlock, StudentEntryBlock, LinkBlock, VideoBlock
from core.wagtail_utils import StrictHierarchyPage from core.wagtail_utils import StrictHierarchyPage
from user.models import UserGroup from user.models import UserGroup
@ -35,7 +35,8 @@ class ContentBlock(StrictHierarchyPage):
('student_entry', StudentEntryBlock(icon='download')), ('student_entry', StudentEntryBlock(icon='download')),
('image_block', ImageChooserBlock(icon='image')), ('image_block', ImageChooserBlock(icon='image')),
('link_block', LinkBlock(icon='link')), ('link_block', LinkBlock(icon='link')),
('task', TextBlock(icon='tick')) ('task', TextBlock(icon='tick')),
('video_block', VideoBlock(icon='media')),
], null=True, blank=True) ], null=True, blank=True)
type = models.CharField( type = models.CharField(

View File

@ -31,7 +31,11 @@ def handle_content_blocks(content_data):
elif content['type'] == 'task': elif content['type'] == 'task':
pass pass
elif content['type'] == 'video_block': elif content['type'] == 'video_block':
pass new_contents.append({
'type': 'video_block',
'value': {
'url': bleach.clean(content['url'])
}})
elif content['type'] == 'document_block': elif content['type'] == 'document_block':
pass pass
return new_contents return new_contents