Merge pull request #1852 from Human-Connection/Allow_embedded_code_in_posts_permanent_memory-II

Save user setting to show embed code II
This commit is contained in:
mattwr18 2019-10-10 14:58:49 +02:00 committed by GitHub
commit 6955323f51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 924 additions and 39 deletions

View File

@ -0,0 +1,257 @@
[
{
"provider_name": "Codepen",
"provider_url": "https:\/\/codepen.io",
"endpoints": [
{
"schemes": [
"http:\/\/codepen.io\/*",
"https:\/\/codepen.io\/*"
],
"url": "http:\/\/codepen.io\/api\/oembed"
}
]
},
{
"provider_name": "DTube",
"provider_url": "https:\/\/d.tube\/",
"endpoints": [
{
"schemes": [
"https:\/\/d.tube\/v\/*"
],
"url": "https:\/\/api.d.tube\/oembed",
"discovery": true
}
]
},
{
"provider_name": "Facebook (Post)",
"provider_url": "https:\/\/www.facebook.com\/",
"endpoints": [
{
"schemes": [
"https:\/\/www.facebook.com\/*\/posts\/*",
"https:\/\/www.facebook.com\/photos\/*",
"https:\/\/www.facebook.com\/*\/photos\/*",
"https:\/\/www.facebook.com\/photo.php*",
"https:\/\/www.facebook.com\/photo.php",
"https:\/\/www.facebook.com\/*\/activity\/*",
"https:\/\/www.facebook.com\/permalink.php",
"https:\/\/www.facebook.com\/media\/set?set=*",
"https:\/\/www.facebook.com\/questions\/*",
"https:\/\/www.facebook.com\/notes\/*\/*\/*"
],
"url": "https:\/\/www.facebook.com\/plugins\/post\/oembed.json",
"discovery": true
}
]
},
{
"provider_name": "Facebook (Video)",
"provider_url": "https:\/\/www.facebook.com\/",
"endpoints": [
{
"schemes": [
"https:\/\/www.facebook.com\/*\/videos\/*",
"https:\/\/www.facebook.com\/video.php"
],
"url": "https:\/\/www.facebook.com\/plugins\/video\/oembed.json",
"discovery": true
}
]
},
{
"provider_name": "Flickr",
"provider_url": "https:\/\/www.flickr.com\/",
"endpoints": [
{
"schemes": [
"http:\/\/*.flickr.com\/photos\/*",
"http:\/\/flic.kr\/p\/*",
"https:\/\/*.flickr.com\/photos\/*",
"https:\/\/flic.kr\/p\/*"
],
"url": "https:\/\/www.flickr.com\/services\/oembed\/",
"discovery": true
}
]
},
{
"provider_name": "GIPHY",
"provider_url": "https:\/\/giphy.com",
"endpoints": [
{
"schemes": [
"https:\/\/giphy.com\/gifs\/*",
"http:\/\/gph.is\/*",
"https:\/\/media.giphy.com\/media\/*\/giphy.gif"
],
"url": "https:\/\/giphy.com\/services\/oembed",
"discovery": true
}
]
},
{
"provider_name": "Instagram",
"provider_url": "https:\/\/instagram.com",
"endpoints": [
{
"schemes": [
"http:\/\/instagram.com\/p\/*",
"http:\/\/instagr.am\/p\/*",
"http:\/\/www.instagram.com\/p\/*",
"http:\/\/www.instagr.am\/p\/*",
"https:\/\/instagram.com\/p\/*",
"https:\/\/instagr.am\/p\/*",
"https:\/\/www.instagram.com\/p\/*",
"https:\/\/www.instagr.am\/p\/*"
],
"url": "https:\/\/api.instagram.com\/oembed",
"formats": [
"json"
]
}
]
},
{
"provider_name": "Meetup",
"provider_url": "http:\/\/www.meetup.com",
"endpoints": [
{
"schemes": [
"http:\/\/meetup.com\/*",
"https:\/\/www.meetup.com\/*",
"https:\/\/meetup.com\/*",
"http:\/\/meetu.ps\/*"
],
"url": "https:\/\/api.meetup.com\/oembed",
"formats": [
"json"
]
}
]
},
{
"provider_name": "MixCloud",
"provider_url": "https:\/\/mixcloud.com\/",
"endpoints": [
{
"schemes": [
"http:\/\/www.mixcloud.com\/*\/*\/",
"https:\/\/www.mixcloud.com\/*\/*\/"
],
"url": "https:\/\/www.mixcloud.com\/oembed\/"
}
]
},
{
"provider_name": "Reddit",
"provider_url": "https:\/\/reddit.com\/",
"endpoints": [
{
"schemes": [
"https:\/\/reddit.com\/r\/*\/comments\/*\/*",
"https:\/\/www.reddit.com\/r\/*\/comments\/*\/*"
],
"url": "https:\/\/www.reddit.com\/oembed"
}
]
},
{
"provider_name": "SlideShare",
"provider_url": "http:\/\/www.slideshare.net\/",
"endpoints": [
{
"schemes": [
"http:\/\/www.slideshare.net\/*\/*",
"http:\/\/fr.slideshare.net\/*\/*",
"http:\/\/de.slideshare.net\/*\/*",
"http:\/\/es.slideshare.net\/*\/*",
"http:\/\/pt.slideshare.net\/*\/*"
],
"url": "http:\/\/www.slideshare.net\/api\/oembed\/2",
"discovery": true
}
]
},
{
"provider_name": "SoundCloud",
"provider_url": "http:\/\/soundcloud.com\/",
"endpoints": [
{
"schemes": [
"http:\/\/soundcloud.com\/*",
"https:\/\/soundcloud.com\/*"
],
"url": "https:\/\/soundcloud.com\/oembed"
}
]
},
{
"provider_name": "Twitch",
"provider_url": "https:\/\/www.twitch.tv",
"endpoints": [
{
"schemes": [
"http:\/\/clips.twitch.tv\/*",
"https:\/\/clips.twitch.tv\/*",
"http:\/\/www.twitch.tv\/*",
"https:\/\/www.twitch.tv\/*",
"http:\/\/twitch.tv\/*",
"https:\/\/twitch.tv\/*"
],
"url": "https:\/\/api.twitch.tv\/v4\/oembed",
"formats": [
"json"
]
}
]
},
{
"provider_name": "Twitter",
"provider_url": "http:\/\/www.twitter.com\/",
"endpoints": [
{
"schemes": [
"https:\/\/twitter.com\/*\/status\/*",
"https:\/\/*.twitter.com\/*\/status\/*"
],
"url": "https:\/\/publish.twitter.com\/oembed"
}
]
},
{
"provider_name": "Vimeo",
"provider_url": "https:\/\/vimeo.com\/",
"endpoints": [
{
"schemes": [
"https:\/\/vimeo.com\/*",
"https:\/\/vimeo.com\/album\/*\/video\/*",
"https:\/\/vimeo.com\/channels\/*\/*",
"https:\/\/vimeo.com\/groups\/*\/videos\/*",
"https:\/\/vimeo.com\/ondemand\/*\/*",
"https:\/\/player.vimeo.com\/video\/*"
],
"url": "https:\/\/vimeo.com\/api\/oembed.{format}",
"discovery": true
}
]
},
{
"provider_name": "YouTube",
"provider_url": "https:\/\/www.youtube.com\/",
"endpoints": [
{
"schemes": [
"https:\/\/*.youtube.com\/watch*",
"https:\/\/*.youtube.com\/v\/*",
"https:\/\/youtu.be\/*"
],
"url": "https:\/\/www.youtube.com\/oembed",
"discovery": true
}
]
}
]

View File

@ -105,4 +105,8 @@ module.exports = {
target: 'Location',
direction: 'out',
},
allowEmbedIframes: {
type: 'boolean',
default: false,
},
}

View File

@ -3,6 +3,7 @@ import path from 'path'
import minimatch from 'minimatch'
let oEmbedProvidersFile = fs.readFileSync(path.join(__dirname, './providers.json'), 'utf8')
// some providers allow a format parameter
// we need JSON
oEmbedProvidersFile = oEmbedProvidersFile.replace(/\{format\}/g, 'json')

View File

@ -176,6 +176,7 @@ export default {
'about',
'termsAndConditionsAgreedVersion',
'termsAndConditionsAgreedAt',
'allowEmbedIframes',
],
boolean: {
followedByCurrentUser:

View File

@ -86,6 +86,7 @@ describe('UpdateUser', () => {
name: 'John Doe',
termsAndConditionsAgreedVersion: null,
termsAndConditionsAgreedAt: null,
allowEmbedIframes: false,
}
variables = {

View File

@ -27,6 +27,8 @@ type User {
termsAndConditionsAgreedVersion: String
termsAndConditionsAgreedAt: String
allowEmbedIframes: Boolean
friends: [User]! @relation(name: "FRIENDS", direction: "BOTH")
friendsCount: Int! @cypher(statement: "MATCH (this)<-[: FRIENDS]->(r: User) RETURN COUNT(DISTINCT r)")
@ -166,6 +168,7 @@ type Mutation {
about: String
termsAndConditionsAgreedVersion: String
termsAndConditionsAgreedAt: String
allowEmbedIframes: Boolean
): User
DeleteUser(id: ID!, resource: [Deletable]): User

View File

@ -16,6 +16,7 @@ export default function create() {
about: faker.lorem.paragraph(),
termsAndConditionsAgreedVersion: '0.0.1',
termsAndConditionsAgreedAt: '2019-08-01T10:47:19.212Z',
allowEmbedIframes: false,
}
defaults.slug = slugify(defaults.name, { lower: true })
args = {

View File

@ -5,8 +5,12 @@ import helpers from '~/storybook/helpers'
import Vue from 'vue'
const embed = {
image: 'https://i.ytimg.com/vi/ptCcgLM-p8k/maxresdefault_live.jpg',
title: 'Video Titel',
// html: null,
description: 'Video Description',
html:
'<iframe width="480" height="270" src="https://www.youtube.com/embed/qkdXAtO40Fo?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>',
'<iframe width="auto" height="250" src="https://www.youtube.com/embed/qkdXAtO40Fo?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>',
}
const plugins = [
@ -114,15 +118,12 @@ storiesOf('Editor', module)
}),
template: `<hc-editor :users="users" :value="content" />`,
}))
.add('Embeds', () => ({
.add('Embeds with iframe', () => ({
components: { HcEditor },
store: helpers.store,
data: () => ({
users,
content: `
<p>
The following link should render a youtube video in addition to the link.
</p>
<a class="embed" href="https://www.youtube.com/watch?v=qkdXAtO40Fo">
<em>https://www.youtube.com/watch?v=qkdXAtO40Fo</em>
</a>
@ -130,3 +131,16 @@ storiesOf('Editor', module)
}),
template: `<hc-editor :users="users" :value="content" />`,
}))
.add('Embeds with plain link', () => ({
components: { HcEditor },
store: helpers.store,
data: () => ({
users,
content: `
<a class="embed" href="https://telegram.org/">
<em>https://telegram.org/</em>
</a>
`,
}),
template: `<hc-editor :users="users" :value="content" />`,
}))

View File

@ -342,4 +342,84 @@ li > p {
margin: 0 0 $space-x-small;
}
}
.ProseMirror[contenteditable='false'] {
.embed-close-button {
display: none;
}
}
.embed-container {
position: relative;
padding: 0;
margin: $space-small auto;
overflow: hidden;
border-radius: $border-radius-base;
border: 1px solid $color-neutral-70;
background-color: $color-neutral-90;
}
.embed-content {
width: 100%;
height: 100%;
h4 {
margin: $space-small 0 0 $space-small;
}
p,
a {
display: block;
margin: 0 0 0 $space-small;
}
}
.embed-preview-image {
width: 100%;
height: auto;
}
.embed-preview-image--clickable {
cursor: pointer;
}
.embed-html {
width: 100%;
iframe {
width: 100%;
}
}
.embed-overlay {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
padding: $space-large;
background-color: $color-neutral-100;
}
.embed-buttons {
button {
margin-right: $space-small;
}
}
.embed-checkbox {
display: flex;
input {
margin-right: $space-small;
}
}
.embed-close-button {
position: absolute;
top: $space-x-small;
right: $space-x-small;
background-color: rgba(250, 249, 250, 0.6);
}
</style>

View File

@ -35,7 +35,7 @@ describe('defaultExtensions', () => {
it('renders mentioning as link', () => {
const editor = createEditor()
const expected =
'<p>This is a post content mentioning <a href="/profile/f0628376-e692-4167-bdb4-d521de5a014f" rel="noopener noreferrer nofollow">@alicia-luettgen</a>.</p>'
'<p>This is a post content mentioning <a href="/profile/f0628376-e692-4167-bdb4-d521de5a014f" rel="noopener noreferrer nofollow" target="_blank">@alicia-luettgen</a>.</p>'
expect(editor.getHTML()).toEqual(expected)
})
})
@ -49,7 +49,7 @@ describe('defaultExtensions', () => {
it('renders hashtag as link', () => {
const editor = createEditor()
const expected =
'<p>This is a post content with a hashtag <a href="/search/hashtag/metoo" rel="noopener noreferrer nofollow">#metoo</a>.</p>'
'<p>This is a post content with a hashtag <a href="/search/hashtag/metoo" rel="noopener noreferrer nofollow" target="_blank">#metoo</a>.</p>'
expect(editor.getHTML()).toEqual(expected)
})
})

View File

@ -1,13 +1,12 @@
import { Node } from 'tiptap'
import pasteRule from '../commands/pasteRule'
import { compileToFunctions } from 'vue-template-compiler'
import Vue from 'vue'
import EmbedComponent from '~/components/Embed/EmbedComponent'
Vue.component(EmbedComponent)
const template = `<component :dataEmbedUrl="dataEmbedUrl" :embedData="embedData" :is="componentType" />`
const template = `
<a class="embed" :href="dataEmbedUrl" rel="noopener noreferrer nofollow" target="_blank">
<div v-if="embedHtml" v-html="embedHtml" />
<em> {{ dataEmbedUrl }} </em>
</a>
`
const compiledTemplate = compileToFunctions(template)
export default class Embed extends Node {
@ -67,16 +66,13 @@ export default class Embed extends Node {
embedData: {},
}),
async created() {
if (!this.options) return {}
this.embedData = await this.options.onEmbed({ url: this.dataEmbedUrl })
if (this.options) {
this.embedData = await this.options.onEmbed({ url: this.dataEmbedUrl })
}
},
computed: {
embedClass() {
return this.embedHtml ? 'embed' : ''
},
embedHtml() {
const { html = '' } = this.embedData
return html
componentType() {
return EmbedComponent
},
dataEmbedUrl: {
get() {

View File

@ -1,31 +1,30 @@
import { shallowMount } from '@vue/test-utils'
import { shallowMount, createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import Styleguide from '@human-connection/styleguide'
import Embed from './Embed'
let Wrapper
let propsData
let Wrapper, propsData, component
const someUrl = 'https://www.youtube.com/watch?v=qkdXAtO40Fo'
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(Styleguide)
describe('Embed.vue', () => {
beforeEach(() => {
propsData = {}
const component = new Embed()
Wrapper = ({ mocks, propsData }) => {
component = new Embed()
Wrapper = ({ propsData }) => {
return shallowMount(component.view, { propsData })
}
})
it('renders anchor', () => {
propsData = {
node: { attrs: { href: someUrl } },
}
expect(Wrapper({ propsData }).is('a')).toBe(true)
})
describe('given a href', () => {
describe('onEmbed returned embed data', () => {
beforeEach(() => {
propsData.options = {
onEmbed: () => ({
__typename: 'Embed',
type: 'video',
title: 'Baby Loves Cat',
author: 'Merkley Family',
@ -49,9 +48,7 @@ describe('Embed.vue', () => {
propsData.node = { attrs: { href: 'https://www.youtube.com/watch?v=qkdXAtO40Fo' } }
const wrapper = Wrapper({ propsData })
await wrapper.html()
expect(wrapper.find('div iframe').attributes('src')).toEqual(
'https://www.youtube.com/embed/qkdXAtO40Fo?feature=oembed',
)
expect(wrapper.contains('embed-component-stub')).toBe(true)
})
})

View File

@ -27,6 +27,7 @@ export default class Link extends TipTapLink {
{
...node.attrs,
rel: 'noopener noreferrer nofollow',
target: '_blank',
},
0,
],

View File

@ -10,7 +10,6 @@ export default class EventHandler extends Extension {
new Plugin({
props: {
transformPastedText(text) {
// console.log('#### transformPastedText', text)
return text.trim()
},
transformPastedHTML(html) {
@ -33,7 +32,6 @@ export default class EventHandler extends Extension {
.replace(/<p>(\s*<br ?\/?>\s*)+/gim, '<p>')
// remove additional linebreaks when last child inside p tags
.replace(/(\s*<br ?\/?>\s*)+<\/p>/gim, '</p>')
// console.log('#### transformPastedHTML', html)
return html
},
// transformPasted(slice) {

View File

@ -0,0 +1,206 @@
import { mount, createLocalVue } from '@vue/test-utils'
import Vuex from 'vuex'
import Styleguide from '@human-connection/styleguide'
import EmbedComponent from './EmbedComponent'
let wrapper, propsData, getters, mocks
const someUrl = 'https://www.youtube.com/watch?v=qkdXAtO40Fo'
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(Styleguide)
describe('EmbedComponent.vue', () => {
const Wrapper = () => {
const store = new Vuex.Store({
getters,
})
return mount(EmbedComponent, { propsData, localVue, store, mocks })
}
beforeEach(() => {
mocks = {
$t: a => a,
$apollo: {
mutate: jest
.fn()
.mockResolvedValueOnce({ data: { UpdateUser: { allowEmbedIframes: true } } }),
},
$toast: {
success: jest.fn(),
error: jest.fn(),
},
}
propsData = {}
getters = {
'auth/user': () => {
return { id: 'u5', allowEmbedIframes: false }
},
}
})
describe('given a href only for a link ', () => {
beforeEach(() => {
propsData.embedData = {
__typename: 'Embed',
type: 'link',
title: '👻 ✉️ Bruno... le ciel sur répondeur ! 🔮 🧠 - Clément FREZE',
author: null,
publisher: 'PeerTube.social',
date: null,
description:
'Salut tout le monde ! Aujourdhui, une vidéo sur le scepticisme, nous allons parler médiumnité avec le cas de Bruno CHARVET : « Bruno, un nouveau message ». Merci de rester respectueux dans les commentaires : SOURCES : Les sources des vi...',
url: 'https://peertube.social/videos/watch/f3cb1945-a8f7-481f-a465-946c6f884e50',
image: 'https://peertube.social/static/thumbnails/f3cb1945-a8f7-481f-a465-946c6f884e50.jpg',
audio: null,
video: null,
lang: 'fr',
sources: ['resource', 'oembed'],
html: null,
}
wrapper = Wrapper()
})
it('shows the title', () => {
expect(wrapper.find('h4').text()).toBe(
'👻 ✉️ Bruno... le ciel sur répondeur ! 🔮 🧠 - Clément FREZE',
)
})
it('shows the description', () => {
expect(wrapper.find('.embed-content p').text()).toBe(
'Salut tout le monde ! Aujourdhui, une vidéo sur le scepticisme, nous allons parler médiumnité avec le cas de Bruno CHARVET : « Bruno, un nouveau message ». Merci de rester respectueux dans les commentaires : SOURCES : Les sources des vi...',
)
})
it('shows preview Images for link', () => {
expect(wrapper.find('.embed-preview-image').exists()).toBe(true)
})
})
describe('given a href with embed html', () => {
describe('onEmbed returned title and description', () => {
beforeEach(() => {
propsData.embedData = {
__typename: 'Embed',
title: 'Baby Loves Cat',
description:
'Shes incapable of controlling her limbs when her kitty is around. The obsession grows every day. Ps. Thats a sleep sack shes in. Not a starfish outfit. Al...',
}
wrapper = Wrapper()
})
it('show the title', () => {
expect(wrapper.find('h4').text()).toBe('Baby Loves Cat')
})
it('show the desciption', () => {
expect(wrapper.find('.embed-content p').text()).toBe(
'Shes incapable of controlling her limbs when her kitty is around. The obsession grows every day. Ps. Thats a sleep sack shes in. Not a starfish outfit. Al...',
)
})
describe('onEmbed returned embed data with html', () => {
beforeEach(() => {
propsData.embedData = {
__typename: 'Embed',
type: 'video',
title: 'Baby Loves Cat',
author: 'Merkley Family',
publisher: 'YouTube',
date: '2015-08-16T00:00:00.000Z',
description:
'Shes incapable of controlling her limbs when her kitty is around. The obsession grows every day. Ps. Thats a sleep sack shes in. Not a starfish outfit. Al...',
url: someUrl,
image: 'https://i.ytimg.com/vi/qkdXAtO40Fo/maxresdefault.jpg',
audio: null,
video: null,
lang: 'de',
sources: ['resource', 'oembed'],
html:
'<iframe width="480" height="270" src="https://www.youtube.com/embed/qkdXAtO40Fo?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>',
}
wrapper = Wrapper()
})
it('shows a simple link when a user closes the embed preview', () => {
wrapper.find('.embed-close-button').trigger('click')
expect(wrapper.vm.showLinkOnly).toBe(true)
})
it('opens the data privacy overlay when a user clicks on the preview image', () => {
wrapper.find('.embed-preview-image--clickable').trigger('click')
expect(wrapper.vm.showOverlay).toBe(true)
})
describe('shows iframe', () => {
beforeEach(() => {
wrapper.setData({ showOverlay: true })
})
it('when user agress', () => {
wrapper.find('.ds-button-primary').trigger('click')
expect(wrapper.vm.showEmbed).toBe(true)
})
it('does not show iframe when user clicks to cancel', () => {
wrapper.find('.ds-button-ghost').trigger('click')
expect(wrapper.vm.showEmbed).toBe(false)
})
describe("doesn't set permanently", () => {
beforeEach(() => {
wrapper.find('.ds-button-primary').trigger('click')
})
it("if user doesn't give consent", () => {
expect(wrapper.vm.checkedAlwaysAllowEmbeds).toBe(false)
})
it("doesn't update the user's profile", () => {
expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
})
})
describe('sets permanently', () => {
beforeEach(() => {
wrapper.find('input[type=checkbox]').trigger('click')
wrapper.find('.ds-button-primary').trigger('click')
})
it('changes setting permanetly when user requests', () => {
expect(wrapper.vm.checkedAlwaysAllowEmbeds).toBe(true)
})
it("updates the user's profile", () => {
expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1)
})
})
})
describe('immediately shows', () => {
beforeEach(() => {
getters = {
'auth/user': () => {
return { id: 'u5', allowEmbedIframes: true }
},
}
wrapper = Wrapper()
})
it('sets showEmbed to true', () => {
expect(wrapper.vm.showEmbed).toBe(true)
})
it('the iframe returned from oEmbed', () => {
expect(wrapper.find('iframe').html()).toEqual(propsData.embedData.html)
})
it('does not display image to click', () => {
expect(wrapper.find('.embed-preview-image--clickable').exists()).toBe(false)
})
})
})
})
})
})

View File

@ -0,0 +1,153 @@
<template>
<a v-if="showLinkOnly" :href="dataEmbedUrl" rel="noopener noreferrer nofollow" target="_blank">
{{ dataEmbedUrl }}
</a>
<ds-container v-else width="small" class="embed-container">
<section class="embed-content">
<div v-if="showEmbed" v-html="embedHtml" class="embed-html" />
<template v-else>
<img
v-if="embedHtml && embedImage"
:src="embedImage"
class="embed-preview-image embed-preview-image--clickable"
@click.prevent="openOverlay()"
/>
<img v-else-if="embedImage" :src="embedImage" class="embed-preview-image" />
</template>
<h4 v-if="embedTitle">{{ embedTitle }}</h4>
<p v-if="embedDescription">{{ embedDescription }}</p>
<a class="embed" :href="dataEmbedUrl" rel="noopener noreferrer nofollow" target="_blank">
{{ dataEmbedUrl }}
</a>
</section>
<aside v-if="showOverlay" class="embed-overlay">
<h3>{{ $t('editor.embed.data_privacy_warning') }}</h3>
<ds-text>{{ $t('editor.embed.data_privacy_info') }} {{ embedPublisher }}</ds-text>
<div class="embed-buttons">
<ds-button primary @click.prevent="allowEmbed()">
{{ $t('editor.embed.play_now') }}
</ds-button>
<ds-button ghost @click.prevent="closeOverlay()">{{ $t('actions.cancel') }}</ds-button>
</div>
<label class="embed-checkbox">
<input type="checkbox" v-model="checkedAlwaysAllowEmbeds" />
<span>{{ $t('editor.embed.always_allow') }}</span>
</label>
</aside>
<ds-button
icon="close"
ghost
size="small"
class="embed-close-button"
@click.prevent="removeEmbed()"
/>
</ds-container>
</template>
<script>
import { mapGetters, mapMutations } from 'vuex'
import { allowEmbedIframesMutation } from '~/graphql/User.js'
export default {
name: 'embed-component',
props: {
dataEmbedUrl: {
type: String,
default: '',
},
embedData: {
type: Object,
default() {
return {}
},
},
},
data() {
return {
checkedAlwaysAllowEmbeds: false,
showEmbed: false,
showOverlay: false,
showLinkOnly: false,
}
},
created() {
if (this.currentUser.allowEmbedIframes) {
this.showEmbed = this.currentUser.allowEmbedIframes
this.checkedAlwaysAllowEmbeds = this.currentUser.allowEmbedIframes
}
},
computed: {
...mapGetters({
currentUser: 'auth/user',
}),
embedHtml() {
const { html = '' } = this.embedData
return html
},
embedImage() {
const { image = '' } = this.embedData
return image
},
embedPublisher() {
const { publisher = '' } = this.embedData
return publisher
},
embedTitle() {
const { title = '' } = this.embedData
return title
},
embedAuthor() {
const { author = '' } = this.embedData
return author
},
embedDescription() {
const { description = '' } = this.embedData
return description
},
},
methods: {
...mapMutations({
setCurrentUser: 'auth/SET_USER',
}),
openOverlay() {
this.showOverlay = true
},
closeOverlay() {
this.showOverlay = false
},
allowEmbed() {
this.showEmbed = true
this.closeOverlay()
if (this.checkedAlwaysAllowEmbeds !== this.currentUser.allowEmbedIframes) {
this.updateEmbedSettings(this.checkedAlwaysAllowEmbeds)
}
},
removeEmbed() {
this.showLinkOnly = true
},
async updateEmbedSettings(allowEmbedIframes) {
try {
await this.$apollo.mutate({
mutation: allowEmbedIframesMutation(),
variables: {
id: this.currentUser.id,
allowEmbedIframes,
},
update: (store, { data: { UpdateUser } }) => {
const { allowEmbedIframes } = UpdateUser
this.setCurrentUser({
...this.currentUser,
allowEmbedIframes,
})
},
})
this.$toast.success(this.$t('contribution.success'))
this.showEmbed = this.currentUser.allowEmbedIframes
} catch (err) {
this.$toast.error(err.message)
}
},
},
}
</script>

View File

@ -135,6 +135,17 @@ export const unfollowUserMutation = i18n => {
`
}
export const allowEmbedIframesMutation = () => {
return gql`
mutation($id: ID!, $allowEmbedIframes: Boolean) {
UpdateUser(id: $id, allowEmbedIframes: $allowEmbedIframes) {
id
allowEmbedIframes
}
}
`
}
export const checkSlugAvailableQuery = gql`
query($slug: String!) {
User(slug: $slug) {

View File

@ -108,6 +108,12 @@
"noHashtagsFound": "Keine Hashtags gefunden",
"addHashtag": "Neuer Hashtag",
"addLetter": "Tippe einen Buchstaben"
},
"embed": {
"data_privacy_warning": "Achte auf deine Daten!",
"data_privacy_info": "Deine Daten wurden noch nicht an Drittanbieter weitergegeben. Wenn du dieses Video jetzt abspielst, registriert der folgende Anbieter wahrscheinlich deine Nutzerdaten:",
"play_now": "Jetzt ansehen",
"always_allow": "Inhalte von Drittanbietern immer anzeigen (diese Einstellung kannst du jederzeit ändern)"
}
},
"profile": {
@ -337,6 +343,19 @@
"submitted": "Kommentar Gesendet",
"updated": "Änderungen gespeichert"
},
"allowEmbeds": {
"name": "Drittanbieter",
"info-description": "Wenn du zustimmst werden in den Beiträgen aus der folgenden Liste an Providern Fremdcode von anderen Anbietern (Drittanbietern) in Form von eingebundenen Videos, Bilder oder Text automatisch eingebunden werden.",
"description": "Du hast zugestimmt das in den Beiträgen aus der folgenden Liste an Providern Fremdcode von anderen Anbietern (Drittanbietern) in Form von eingebundenen Videos, Bilder oder Text automatisch eingebunden werden.",
"statustext": "Momentan ist das automatische einbinden:",
"statuschange": "Einstellung ändern",
"false": "Abgestellt",
"true": "Zugelassen",
"button-tofalse": "Abstellen",
"button-totrue": "dauerhaft zulassen",
"third-party-false": "Es wird <b style='color:red'>kein</b> Service von Drittanbietern automatisch eingebunden.",
"third-party-true": "Das einbinden der Services von Drittanbietern ist <b style='color:red'>dauerhaft zugelassen</b> und gespeichert für komende Sitzungen."
},
"edited": "bearbeitet"
},
"comment": {

View File

@ -109,6 +109,12 @@
"noHashtagsFound": "No hashtags found",
"addHashtag": "New hashtag",
"addLetter": "Type a letter"
},
"embed": {
"data_privacy_warning": "Data Privacy Warning!",
"data_privacy_info": "Your data has not yet been shared with any third party providers. If you proceed to watch this video the following provider will likely collect user data:",
"play_now": "Watch now",
"always_allow": "Always allow embedded content by third party providers (this setting can be changed any time)"
}
},
"profile": {
@ -338,6 +344,19 @@
"submitted": "Comment Submitted",
"updated": "Changes Saved"
},
"allowEmbeds": {
"name": "Third party providers",
"info-description": "If you agree, the posts from the following list of providers will automatically include third-party code from other providers (third parties) in the form of embedded videos, images, or text.",
"description": "You have agreed that in the contributions from the following list of providers, foreign code from other providers (third parties) in the form of embedded videos, images or text automatically are embedded.",
"statustext": "At the moment this is automatic embedding:",
"statuschange": "Change setting",
"false": "Turned off",
"true": "Admitted",
"button-tofalse": "turn-off",
"button-totrue": "allow permanently",
"third-party-false": "It automatically integrates <b style='color:red'>no </b> third-party providers' service.",
"third-party-true": "The inclusion of third-party services is <b style='color:red'>permanently allowed</b> and stored for future sessions."
},
"edited": "edited"
},
"comment": {

View File

@ -6,6 +6,7 @@ export default async ({ store, env, route, redirect }) => {
if (publicPages.indexOf(route.name) >= 0) {
return true
}
if (route.name === 'terms-and-conditions-confirm') return true // avoid endless loop
if (store.getters['auth/termsAndConditionsAgreed']) return true

View File

@ -133,4 +133,4 @@
"vue-svg-loader": "~0.12.0",
"vue-template-compiler": "^2.6.10"
}
}
}

View File

@ -39,6 +39,10 @@ export default {
name: this.$t('settings.blocked-users.name'),
path: `/settings/blocked-users`,
},
{
name: this.$t('post.allowEmbeds.name'),
path: `/settings/allow-embeds`,
},
{
name: this.$t('settings.deleteUserAccount.name'),
path: `/settings/delete-account`,

View File

@ -0,0 +1,117 @@
<template>
<ds-card :header="$t('post.allowEmbeds.name')">
<p>{{ $t('post.allowEmbeds.statustext') }}</p>
<ds-container width="large">
<h3 v-html="allowEmbeds_h3" />
<div v-html="allowEmbeds_desc" />
</ds-container>
<div>
<ds-space />
<ds-space />
<p>{{ $t('post.allowEmbeds.statuschange') }}</p>
<ds-container width="large">
<ds-flex>
<ds-flex-item>
<ds-button @click="toFalse" :disabled="!disabled">
{{ $t('post.allowEmbeds.button-tofalse') }}
</ds-button>
</ds-flex-item>
<ds-flex-item>
<ds-button @click="toTrue" secondary :disabled="disabled">
{{ $t('post.allowEmbeds.button-totrue') }}
</ds-button>
</ds-flex-item>
</ds-flex>
</ds-container>
</div>
<ds-space />
<ds-space />
<div>
<p v-if="disabled">
<b>{{ $t('post.allowEmbeds.description') }}</b>
</p>
<p v-else>{{ $t('post.allowEmbeds.info-description') }}</p>
<ds-container>
<ds-placeholder>
<ul>
<li v-for="provider in providers" :key="provider.provider_name">
{{ provider.provider_name }},
<small>{{ provider.provider_url }}</small>
</li>
</ul>
</ds-placeholder>
</ds-container>
</div>
</ds-card>
</template>
<script>
import axios from 'axios'
import { mapGetters, mapMutations } from 'vuex'
import { allowEmbedIframesMutation } from '~/graphql/User.js'
export default {
head() {
return {
title: this.$t('post.allowEmbeds.name'),
}
},
computed: {
...mapGetters({
currentUser: 'auth/user',
}),
},
data() {
return {
allowEmbeds_h3: this.$t('post.allowEmbeds.false'),
allowEmbeds_desc: this.$t('post.allowEmbeds.third-party-false'),
disabled: null,
providers: [],
}
},
mounted() {
axios.get('../api/providers.json').then(response => {
this.providers = response.data
})
this.disabled = this.currentUser.allowEmbedIframes
},
methods: {
...mapMutations({
setCurrentUser: 'auth/SET_USER',
}),
toFalse() {
this.allowEmbeds_h3 = this.$t('post.allowEmbeds.false')
this.allowEmbeds_desc = this.$t('post.allowEmbeds.third-party-false')
this.submit()
},
toTrue() {
this.allowEmbeds_h3 = this.$t('post.allowEmbeds.true')
this.allowEmbeds_desc = this.$t('post.allowEmbeds.third-party-true')
this.submit()
},
async submit() {
try {
await this.$apollo.mutate({
mutation: allowEmbedIframesMutation(),
variables: {
id: this.currentUser.id,
allowEmbedIframes: !this.disabled,
},
update: (store, { data: { UpdateUser } }) => {
const { allowEmbedIframes } = UpdateUser
this.setCurrentUser({
...this.currentUser,
allowEmbedIframes,
})
},
})
this.$toast.success(this.$t('site.thanks') + ' ' + this.allowEmbeds_h3)
this.disabled = !this.disabled
} catch (err) {
this.$toast.error(err.message)
}
},
},
}
</script>

View File

@ -86,6 +86,7 @@ export const actions = {
locationName
contributionsCount
commentedCount
allowEmbedIframes
termsAndConditionsAgreedVersion
socialMedia {
id