Merge branch 'feature/image-upload'
This commit is contained in:
commit
5d52aef859
|
|
@ -0,0 +1,4 @@
|
|||
# Do not edit this file. To specify the files to encrypt, create your own
|
||||
# .gitattributes file in the directory where your files are.
|
||||
* !filter !diff
|
||||
*.gpg binary
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
…<01>™'G©<47>þ£n”~DùÎMÌHàŠ$L¥)kš~lƒ"i-Y%<25>7ß2%
|
||||
1*&·Šš¤ç¥X—ò:ãÂn •2pRò4™zh×(sË£ÃR±ú"<22>k|¹:iN{Ýcˆòýó[,>‹ö’¼?šÝ³lÛ1%%än¾È§ïÓxاô¨K„û´¯%õ¤K#ö‹Ô8$Úïo 1LsЧŸӹ¢†²QÉ`Šé h§*(LËóp#õÈz<C388>á1º¢
<0A>›ešV°e‘8ÞõM¨pç$ð’c2)(‰°1‹ú&«»lþÒ<Ù
|
||||
<î±ëW¢•q¶Tƒ\÷÷6÷y0CÁIjyÜFûþO<C3BE>‘žñZ”ŒÇ;ðïNoIŠÚ•†™ç?ßf ĦÁf(g&#a^d¥-ä!ÄTDöŠ2&®Þ•@9{OÁnjšÔyø%WšƒÛÃñá(S´ó¬Å&Цìf<>ûÈ–Ç+ø¡(žìõdRú/Ž>â¡Â1ÅÆ’<>òtÀkëL¾2´ÛÒÀ)¨÷_ È›‚رF˜,Š×û<C397>Êa<1B>寅—è:OJ¿‚ÿø’ªÔõ§Â_EfžÍƒ1t[¡Ô—Ý:L£=¨R5‹‹•o±Vôp—45
|
||||
ïÝx<EFBFBD>¯”N7oÇ›œc•ànI…Ÿ×,ƒ4ÝÍ—¤XUÙš˜üËçHy¢¤v>YAÍÜ¢›Ó¿ý‘¢vSƺúöû@ÐøýŠ&óë”?B2pH<70>ã=¯€ñÜöüûw'læŠïŽÀíˆÆï$f àaOiLGÛv`t
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -27,6 +27,7 @@ module.exports = {
|
|||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||
'indent': 'off',
|
||||
'semi': 0,
|
||||
'space-before-function-paren': 'off'
|
||||
'space-before-function-paren': 'off',
|
||||
'comma-dangle': 'off',
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,6 +8,21 @@
|
|||
<link href='https://fonts.googleapis.com/css?family=Material+Icons' rel="stylesheet" type="text/css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,500,800" rel="stylesheet">
|
||||
<link href="https://use.typekit.net/tck7ptw.css" rel="stylesheet">
|
||||
|
||||
<script>
|
||||
window.UPLOADCARE_LOCALE = 'de';
|
||||
window.UPLOADCARE_LOCALE_TRANSLATIONS = {
|
||||
dialog: {
|
||||
tabs: {
|
||||
file: {
|
||||
drag: 'Ziehen Sie ein Bild hier hinein',
|
||||
button: 'Wählen Sie ein lokales Bild',
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
import BasicKnowledgeWidget from '@/components/content-blocks/BasicKnowledgeWidget';
|
||||
import Task from '@/components/content-blocks/Task';
|
||||
import ImageBlock from '@/components/content-blocks/ImageBlock';
|
||||
import ImageUrlBlock from '@/components/content-blocks/ImageUrlBlock';
|
||||
import VideoBlock from '@/components/content-blocks/VideoBlock';
|
||||
import LinkBlock from '@/components/content-blocks/LinkBlock';
|
||||
import DocumentBlock from '@/components/content-blocks/DocumentBlock';
|
||||
|
|
@ -53,6 +54,7 @@
|
|||
'basic_knowledge': BasicKnowledgeWidget,
|
||||
'student_entry': StudentEntry,
|
||||
'image_block': ImageBlock,
|
||||
'image_url_block': ImageUrlBlock,
|
||||
'video_block': VideoBlock,
|
||||
'link_block': LinkBlock,
|
||||
'document_block': DocumentBlock,
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<modal>
|
||||
<content-block-title-input slot="header" v-on:update-title="updateTitle" :title="contentBlock.title"
|
||||
<content-block-title-input slot="header" v-on:update-title="updateTitle" :title="localContentBlock.title"
|
||||
:error="error"></content-block-title-input>
|
||||
<add-content-element class="content-block-form__add"
|
||||
v-on:add-element="addElement"
|
||||
:index="-1"
|
||||
></add-content-element>
|
||||
<div v-for="(element, index) in contentBlock.contents" :key="index" class="content-block-form__element">
|
||||
<div v-for="(element, index) in localContentBlock.contents" :key="index" class="content-block-form__element">
|
||||
<component
|
||||
class="content-block-form__element-component"
|
||||
:is="type(element)"
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
v-on:link-change-text="changeLinkText"
|
||||
v-on:text-change-value="changeTextValue"
|
||||
v-on:document-change-url="changeDocumentUrl"
|
||||
v-on:image-change-url="changeImageUrl"
|
||||
v-on:video-change-url="changeVideoUrl">
|
||||
</component>
|
||||
<a class="content-block-form__remove" v-on:click="removeElement(index)">
|
||||
|
|
@ -70,7 +71,7 @@
|
|||
data() {
|
||||
return {
|
||||
error: false,
|
||||
localContentBlock: Object.assign({}, this.contentBlock)
|
||||
localContentBlock: JSON.parse(JSON.stringify(this.contentBlock))
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -81,7 +82,7 @@
|
|||
return 'link-form';
|
||||
case 'video_block':
|
||||
return 'video-form';
|
||||
case 'image_block':
|
||||
case 'image_url_block':
|
||||
return 'image-form';
|
||||
case 'text_block':
|
||||
return 'text-form';
|
||||
|
|
@ -112,6 +113,9 @@
|
|||
changeVideoUrl(value, index) {
|
||||
this._updateProperty(value, index, 'url')
|
||||
},
|
||||
changeImageUrl(value, index) {
|
||||
this._updateProperty(value, index, 'url')
|
||||
},
|
||||
changeDocumentUrl(value, index) {
|
||||
this._updateProperty(value, index, 'url')
|
||||
},
|
||||
|
|
@ -167,6 +171,14 @@
|
|||
}
|
||||
};
|
||||
break;
|
||||
case 'image_url_block':
|
||||
el = {
|
||||
...el,
|
||||
value: {
|
||||
url: ''
|
||||
}
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
this.localContentBlock.contents.splice(index, 1, el);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
<template>
|
||||
<img :src="value.url" alt="" class="image-block">
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['value']
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.image-block {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
border-radius: 13px;
|
||||
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
<video-icon class="content-block-element-chooser-widget__link-icon"></video-icon>
|
||||
<div class="content-block-element-chooser-widget__link-title">Video</div>
|
||||
</div>
|
||||
<div class="content-block-element-chooser-widget__link" v-on:click="$emit('change-type', index, 'image_block')">
|
||||
<div class="content-block-element-chooser-widget__link" v-on:click="$emit('change-type', index, 'image_url_block')">
|
||||
<image-icon class="content-block-element-chooser-widget__link-icon"></image-icon>
|
||||
<div class="content-block-element-chooser-widget__link-title">Bild</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,56 @@
|
|||
<template>
|
||||
<div class="image-form">
|
||||
<input id="image-input" type="file" class="image-form__file-input">
|
||||
<label class="image-form__button" for="image-input">Bild vom Computer hochladen</label>
|
||||
<div v-if="!value.url" ref="uploadcare-panel"></div>
|
||||
<div v-if="value.url">
|
||||
<img :src="previewUrl">
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import uploadcare from 'uploadcare-widget';
|
||||
|
||||
export default {
|
||||
|
||||
props: ['value', 'index'],
|
||||
mounted() {
|
||||
let uploadcarePanel = openImagePanel(this.$refs['uploadcare-panel']);
|
||||
|
||||
uploadcarePanel.done(panelResult => {
|
||||
console.log(panelResult);
|
||||
|
||||
panelResult.done(fileInfo => {
|
||||
console.log(fileInfo);
|
||||
this.$emit('link-change-url', fileInfo.cdnUrl, this.index)
|
||||
});
|
||||
|
||||
panelResult.progress(p => {
|
||||
console.log(p);
|
||||
});
|
||||
|
||||
panelResult.fail(uploadResult => {
|
||||
console.log('fail');
|
||||
console.log(uploadResult);
|
||||
});
|
||||
});
|
||||
|
||||
function openImagePanel(panelElement) {
|
||||
return uploadcare.openPanel(panelElement, null, {
|
||||
tabs: ['file'],
|
||||
publicKey: '78212ff39934a59775ac',
|
||||
});
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
previewUrl: function() {
|
||||
console.log(this.value);
|
||||
if (this.value && this.value.url) {
|
||||
return this.value.url + '-/preview/200x200/';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
.uploadcare--panel {
|
||||
background: $color-lightgrey;
|
||||
height: 200px;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.uploadcare--menu {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.uploadcare--button {
|
||||
background: none !important;
|
||||
color: inherit;
|
||||
border: none;
|
||||
padding: 0 !important;
|
||||
font: inherit;
|
||||
border-bottom: 1px solid #444;
|
||||
cursor: pointer;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.uploadcare--button_primary:hover {
|
||||
background: inherit;
|
||||
border-color: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.uploadcare--text_size_extra-large {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
|
@ -8,9 +8,5 @@
|
|||
@import "variables";
|
||||
@import "buttons";
|
||||
@import "forms";
|
||||
@import "uploadcare_overwrite";
|
||||
@import "help-text";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
* filter=git-crypt diff=git-crypt
|
||||
.gitattributes !filter !diff
|
||||
Binary file not shown.
|
|
@ -35,6 +35,11 @@
|
|||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz",
|
||||
"integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw=="
|
||||
},
|
||||
"jquery": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.3.1.tgz",
|
||||
"integrity": "sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg=="
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.10.5",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz",
|
||||
|
|
@ -44,6 +49,14 @@
|
|||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/unfetch/-/unfetch-3.1.1.tgz",
|
||||
"integrity": "sha512-syDl3htvM56w0HC0PTVA5jEEknOCJ3dWgWGDuaEtQUno8ORDCfZQbm12RzfWO3AC3YhWDoP61dlgmo8Z05Y97g=="
|
||||
},
|
||||
"uploadcare-widget": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/uploadcare-widget/-/uploadcare-widget-3.6.0.tgz",
|
||||
"integrity": "sha512-QWvZtPG35ndZJfNxKSu+rrzgIPK+UhO4KzWntP9zfJBvaT1xdbIIF/OMYBhNFG5dbw5j7qFIS2SJvtZZAK9rfw==",
|
||||
"requires": {
|
||||
"jquery": ">=1.10.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"unfetch": "^3.0.0"
|
||||
"unfetch": "^3.0.0",
|
||||
"uploadcare-widget": "3.6.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
# Generated by Django 2.0.6 on 2018-09-18 16:05
|
||||
|
||||
from django.db import migrations
|
||||
import wagtail.core.blocks
|
||||
import wagtail.core.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('book', '0002_auto_20180913_0738'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='contentblock',
|
||||
name='contents',
|
||||
field=wagtail.core.fields.StreamField([('text_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock())], icon='doc-full')), ('basic_knowledge', wagtail.core.blocks.StructBlock([('description', wagtail.core.blocks.RichTextBlock()), ('url', wagtail.core.blocks.URLBlock())], icon='placeholder')), ('student_entry', wagtail.core.blocks.StructBlock([('task_text', wagtail.core.blocks.RichTextBlock())], icon='download')), ('image_block', wagtail.core.blocks.StructBlock([('title', wagtail.core.blocks.RichTextBlock()), ('url', wagtail.core.blocks.URLBlock())], icon='image')), ('link_block', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.TextBlock()), ('url', wagtail.core.blocks.URLBlock())], icon='link')), ('task', wagtail.core.blocks.StructBlock([('text', wagtail.core.blocks.RichTextBlock())], icon='tick')), ('video_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())], icon='media')), ('document_block', wagtail.core.blocks.StructBlock([('url', wagtail.core.blocks.URLBlock())], icon='doc-full'))], blank=True, null=True),
|
||||
),
|
||||
]
|
||||
|
|
@ -5,7 +5,8 @@ from wagtail.admin.edit_handlers import FieldPanel, TabbedInterface, ObjectList,
|
|||
from wagtail.core.fields import StreamField
|
||||
from wagtail.images.blocks import ImageChooserBlock
|
||||
|
||||
from book.blocks import TextBlock, BasicKnowledgeBlock, StudentEntryBlock, LinkBlock, VideoBlock, DocumentBlock
|
||||
from book.blocks import TextBlock, BasicKnowledgeBlock, StudentEntryBlock, LinkBlock, VideoBlock, DocumentBlock, \
|
||||
ImageUrlBlock
|
||||
from core.wagtail_utils import StrictHierarchyPage
|
||||
from user.models import UserGroup
|
||||
|
||||
|
|
@ -36,6 +37,7 @@ class ContentBlock(StrictHierarchyPage):
|
|||
('basic_knowledge', BasicKnowledgeBlock(icon='placeholder')),
|
||||
('student_entry', StudentEntryBlock(icon='download')),
|
||||
('image_block', ImageChooserBlock(icon='image')),
|
||||
('image_url_block', ImageUrlBlock(icon='image')),
|
||||
('link_block', LinkBlock(icon='link')),
|
||||
('task', TextBlock(icon='tick')),
|
||||
('video_block', VideoBlock(icon='media')),
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ class InputTypes(graphene.Enum):
|
|||
# basic_knowledge = 'basic_knowledge' # probably won't be using this over the API
|
||||
student_entry = 'student_entry'
|
||||
image_block = 'image_block'
|
||||
image_url_block = 'image_url_block'
|
||||
link_block = 'link_block'
|
||||
task = 'task'
|
||||
video_block = 'video_block'
|
||||
|
|
|
|||
|
|
@ -25,8 +25,12 @@ def handle_content_blocks(content_data):
|
|||
}})
|
||||
elif content['type'] == 'student_entry':
|
||||
pass
|
||||
elif content['type'] == 'image_block':
|
||||
pass
|
||||
elif content['type'] == 'image_url_block':
|
||||
new_contents.append({
|
||||
'type': 'image_url_block',
|
||||
'value': {
|
||||
'url': bleach.clean(content['value']['url'])
|
||||
}})
|
||||
elif content['type'] == 'link_block':
|
||||
new_contents.append({
|
||||
'type': 'link_block',
|
||||
|
|
|
|||
|
|
@ -1,8 +1,4 @@
|
|||
import io
|
||||
import os
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
from graphene.test import Client
|
||||
from graphql_relay import to_global_id
|
||||
|
||||
|
|
@ -11,8 +7,6 @@ from api.utils import get_graphql_mutation, get_object
|
|||
from book.factories import ContentBlockFactory
|
||||
from book.models import ContentBlock
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class NewContentBlockMutationTest(TestCase):
|
||||
def setUp(self):
|
||||
|
|
@ -52,3 +46,35 @@ class NewContentBlockMutationTest(TestCase):
|
|||
content_block_page = get_object(ContentBlock, id)
|
||||
|
||||
self.assertEqual(content_block_page.title, title)
|
||||
|
||||
def test_addNewContentBlock_withImageUrlBlock(self):
|
||||
self.assertEqual(ContentBlock.objects.count(), 1)
|
||||
client = Client(schema=schema)
|
||||
|
||||
mutation = get_graphql_mutation('addContentBlock.gql')
|
||||
|
||||
title = "Hello World"
|
||||
|
||||
result = client.execute(mutation, variables={
|
||||
'input': {
|
||||
"contentBlock": {
|
||||
"title": title,
|
||||
"contents": [
|
||||
{
|
||||
"type": "image_url_block",
|
||||
'value': {
|
||||
"url": "/test.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"after": self.sibling_id
|
||||
}
|
||||
})
|
||||
self.assertIsNone(result.get('errors'))
|
||||
self.assertEqual(ContentBlock.objects.count(), 2)
|
||||
|
||||
new_content_block = result.get('data').get('addContentBlock').get('newContentBlock')
|
||||
self.assertEqual(new_content_block.get('title'), title)
|
||||
self.assertEqual(len(new_content_block['contents']), 1)
|
||||
self.assertEqual(new_content_block['contents'], [{'type': 'image_url_block', 'value': {'url': '/test.png'}}])
|
||||
|
|
|
|||
Loading…
Reference in New Issue