mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-13 07:46:06 +00:00
Merge branch 'master' of github.com:Human-Connection/Human-Connection into 1707-reporting-with-specific-information
# Conflicts: # webapp/locales/de.json
This commit is contained in:
commit
2a034ee74f
@ -50,6 +50,8 @@ Join our friendly open-source community on [Discord](https://discordapp.com/invi
|
||||
Just introduce yourself at `#introduce-yourself` and mention `@@Mentor` to get you onboard :neckbeard:
|
||||
Check out the [contribution guideline](./CONTRIBUTING.md), too!
|
||||
|
||||
[](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/0)[](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/1)[](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/2)[](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/3)[](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/4)[](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/5)[](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/6)[](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/7)
|
||||
|
||||
|
||||
## Attributions
|
||||
|
||||
|
||||
@ -17,3 +17,4 @@ PRIVATE_KEY_PASSPHRASE="a7dsf78sadg87ad87sfagsadg78"
|
||||
|
||||
SENTRY_DSN_BACKEND=
|
||||
COMMIT=
|
||||
PUBLIC_REGISTRATION=false
|
||||
|
||||
@ -93,7 +93,7 @@
|
||||
"neo4j-graphql-js": "^2.7.2",
|
||||
"neode": "^0.3.3",
|
||||
"node-fetch": "~2.6.0",
|
||||
"nodemailer": "^6.3.0",
|
||||
"nodemailer": "^6.3.1",
|
||||
"nodemailer-html-to-text": "^3.1.0",
|
||||
"npm-run-all": "~4.1.5",
|
||||
"request": "~2.88.0",
|
||||
|
||||
257
backend/public/providers.json
Normal file
257
backend/public/providers.json
Normal 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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@ -21,7 +21,12 @@ const {
|
||||
GRAPHQL_URI = 'http://localhost:4000',
|
||||
} = process.env
|
||||
|
||||
export const requiredConfigs = { MAPBOX_TOKEN, JWT_SECRET, PRIVATE_KEY_PASSPHRASE }
|
||||
export const requiredConfigs = {
|
||||
MAPBOX_TOKEN,
|
||||
JWT_SECRET,
|
||||
PRIVATE_KEY_PASSPHRASE,
|
||||
}
|
||||
|
||||
export const smtpConfigs = {
|
||||
SMTP_HOST,
|
||||
SMTP_PORT,
|
||||
@ -30,7 +35,12 @@ export const smtpConfigs = {
|
||||
SMTP_PASSWORD,
|
||||
}
|
||||
export const neo4jConfigs = { NEO4J_URI, NEO4J_USERNAME, NEO4J_PASSWORD }
|
||||
export const serverConfigs = { GRAPHQL_PORT, CLIENT_URI, GRAPHQL_URI }
|
||||
export const serverConfigs = {
|
||||
GRAPHQL_PORT,
|
||||
CLIENT_URI,
|
||||
GRAPHQL_URI,
|
||||
PUBLIC_REGISTRATION: process.env.PUBLIC_REGISTRATION === 'true',
|
||||
}
|
||||
|
||||
export const developmentConfigs = {
|
||||
DEBUG: process.env.NODE_ENV !== 'production' && process.env.DEBUG,
|
||||
|
||||
@ -18,7 +18,7 @@ export const signupTemplate = ({ email, nonce }) => {
|
||||
subject,
|
||||
html: mustache.render(
|
||||
templates.layout,
|
||||
{ actionUrl, supportUrl, subject },
|
||||
{ actionUrl, nonce, supportUrl, subject },
|
||||
{ content: templates.signup },
|
||||
),
|
||||
}
|
||||
|
||||
@ -60,7 +60,9 @@
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td style="padding: 20px; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
|
||||
<p style="margin: 0;">Falls Du Dich nicht selbst bei <a href="https://human-connection.org"
|
||||
<p style="margin: 0;">Sollte der Button für Dich nicht funktionieren, kannst Du auch folgenden Code in Dein Browserfenster kopieren: <span style="color: #17b53e;">{{{ nonce }}}</span></p>
|
||||
<p style="margin: 0;">Das funktioniert allerdings nur, wenn du Dich über unsere Website registriert hast.</p>
|
||||
<p style="margin: 0; margin-top: 10px;">Falls Du Dich nicht selbst bei <a href="https://human-connection.org"
|
||||
style="color: #17b53e;">Human Connection</a> angemeldet hast, schau doch mal vorbei!
|
||||
Wir sind ein gemeinnütziges Aktionsnetzwerk – von Menschen für Menschen.</p>
|
||||
<p style="margin: 0; margin-top: 10px;">PS: Wenn Du keinen Account bei uns möchtest, kannst Du diese
|
||||
@ -169,7 +171,9 @@
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tr>
|
||||
<td style="padding: 20px; font-family: Lato, sans-serif; font-size: 16px; line-height: 22px; color: #555555;">
|
||||
<p style="margin: 0;">If you didn't sign up for <a href="https://human-connection.org"
|
||||
<p style="margin: 0;">If the above button doesn't work, you can also copy the following code into your browser window: <span style="color: #17b53e;">{{{ nonce }}}</span></p>
|
||||
<p style="margin: 0;">However, this only works if you have registered through our website.</p>
|
||||
<p style="margin: 0; margin-top: 10px;">If you didn't sign up for <a href="https://human-connection.org"
|
||||
style="color: #17b53e;">Human Connection</a> we recommend you to check it out!
|
||||
It's a social network from people for people who want to connect and change the world together.</p>
|
||||
<p style="margin: 0; margin-top: 10px;">PS: If you ignore this e-mail we will not create an account
|
||||
|
||||
@ -111,6 +111,8 @@ const noEmailFilter = rule({
|
||||
return !('email' in args)
|
||||
})
|
||||
|
||||
const publicRegistration = rule()(() => !!CONFIG.PUBLIC_REGISTRATION)
|
||||
|
||||
// Permissions
|
||||
const permissions = shield(
|
||||
{
|
||||
@ -137,7 +139,7 @@ const permissions = shield(
|
||||
'*': deny,
|
||||
login: allow,
|
||||
SignupByInvitation: allow,
|
||||
Signup: isAdmin,
|
||||
Signup: or(publicRegistration, isAdmin),
|
||||
SignupVerification: allow,
|
||||
CreateInvitationCode: and(isAuthenticated, or(not(invitationLimitReached), isAdmin)),
|
||||
UpdateUser: onlyYourself,
|
||||
|
||||
@ -105,4 +105,8 @@ module.exports = {
|
||||
target: 'Location',
|
||||
direction: 'out',
|
||||
},
|
||||
allowEmbedIframes: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
}
|
||||
|
||||
@ -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')
|
||||
|
||||
@ -176,6 +176,7 @@ export default {
|
||||
'about',
|
||||
'termsAndConditionsAgreedVersion',
|
||||
'termsAndConditionsAgreedAt',
|
||||
'allowEmbedIframes',
|
||||
],
|
||||
boolean: {
|
||||
followedByCurrentUser:
|
||||
|
||||
@ -86,6 +86,7 @@ describe('UpdateUser', () => {
|
||||
name: 'John Doe',
|
||||
termsAndConditionsAgreedVersion: null,
|
||||
termsAndConditionsAgreedAt: null,
|
||||
allowEmbedIframes: false,
|
||||
}
|
||||
|
||||
variables = {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 = {
|
||||
|
||||
@ -6182,10 +6182,10 @@ nodemailer-html-to-text@^3.1.0:
|
||||
dependencies:
|
||||
html-to-text "^5.1.1"
|
||||
|
||||
nodemailer@^6.3.0:
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.3.0.tgz#a89b0c62d3937bdcdeecbf55687bd7911b627e12"
|
||||
integrity sha512-TEHBNBPHv7Ie/0o3HXnb7xrPSSQmH1dXwQKRaMKDBGt/ZN54lvDVujP6hKkO/vjkIYL9rK8kHSG11+G42Nhxuw==
|
||||
nodemailer@^6.3.1:
|
||||
version "6.3.1"
|
||||
resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.3.1.tgz#2784beebac6b9f014c424c54dbdcc5c4d1221346"
|
||||
integrity sha512-j0BsSyaMlyadEDEypK/F+xlne2K5m6wzPYMXS/yxKI0s7jmT1kBx6GEKRVbZmyYfKOsjkeC/TiMVDJBI/w5gMQ==
|
||||
|
||||
nodemon@~1.19.3:
|
||||
version "1.19.3"
|
||||
|
||||
@ -15,6 +15,7 @@ services:
|
||||
- ./webapp:/nitro-web
|
||||
environment:
|
||||
- NUXT_BUILD=/tmp/nuxt # avoid file permission issues when `rm -rf .nuxt/`
|
||||
- PUBLIC_REGISTRATION=true
|
||||
command: yarn run dev
|
||||
backend:
|
||||
build:
|
||||
@ -28,6 +29,7 @@ services:
|
||||
- SMTP_PORT=25
|
||||
- SMTP_IGNORE_TLS=true
|
||||
- "DEBUG=${DEBUG}"
|
||||
- PUBLIC_REGISTRATION=true
|
||||
maintenance:
|
||||
image: humanconnection/maintenance:latest
|
||||
build:
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
"codecov": "^3.6.1",
|
||||
"cross-env": "^6.0.3",
|
||||
"cypress": "^3.4.1",
|
||||
"cypress-cucumber-preprocessor": "^1.16.1",
|
||||
"cypress-cucumber-preprocessor": "^1.16.2",
|
||||
"cypress-file-upload": "^3.3.4",
|
||||
"cypress-plugin-retries": "^1.3.0",
|
||||
"dotenv": "^8.1.0",
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit d46fc1570c6bcea328ae4cc3a4892745edea7319
|
||||
Subproject commit 808b3c5a9523505cb80b20b50348d29ba9932845
|
||||
@ -1,3 +1,4 @@
|
||||
MAPBOX_TOKEN="pk.eyJ1IjoiaHVtYW4tY29ubmVjdGlvbiIsImEiOiJjajl0cnBubGoweTVlM3VwZ2lzNTNud3ZtIn0.KZ8KK9l70omjXbEkkbHGsQ"
|
||||
SENTRY_DSN_WEBAPP=
|
||||
COMMIT=
|
||||
PUBLIC_REGISTRATION=false
|
||||
|
||||
@ -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" />`,
|
||||
}))
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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)
|
||||
})
|
||||
})
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -27,6 +27,7 @@ export default class Link extends TipTapLink {
|
||||
{
|
||||
...node.attrs,
|
||||
rel: 'noopener noreferrer nofollow',
|
||||
target: '_blank',
|
||||
},
|
||||
0,
|
||||
],
|
||||
|
||||
@ -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) {
|
||||
|
||||
206
webapp/components/Embed/EmbedComponent.spec.js
Normal file
206
webapp/components/Embed/EmbedComponent.spec.js
Normal 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 ! Aujourd’hui, 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 ! Aujourd’hui, 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:
|
||||
'She’s incapable of controlling her limbs when her kitty is around. The obsession grows every day. Ps. That’s a sleep sack she’s 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(
|
||||
'She’s incapable of controlling her limbs when her kitty is around. The obsession grows every day. Ps. That’s a sleep sack she’s 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:
|
||||
'She’s incapable of controlling her limbs when her kitty is around. The obsession grows every day. Ps. That’s a sleep sack she’s 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)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
153
webapp/components/Embed/EmbedComponent.vue
Normal file
153
webapp/components/Embed/EmbedComponent.vue
Normal 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>
|
||||
@ -1,12 +1,12 @@
|
||||
import { mount, createLocalVue } from '@vue/test-utils'
|
||||
import VerifyNonce from './VerifyNonce.vue'
|
||||
import EnterNonce from './EnterNonce.vue'
|
||||
import Styleguide from '@human-connection/styleguide'
|
||||
|
||||
const localVue = createLocalVue()
|
||||
|
||||
localVue.use(Styleguide)
|
||||
|
||||
describe('VerifyNonce ', () => {
|
||||
describe('EnterNonce ', () => {
|
||||
let wrapper
|
||||
let Wrapper
|
||||
let mocks
|
||||
@ -25,28 +25,28 @@ describe('VerifyNonce ', () => {
|
||||
beforeEach(jest.useFakeTimers)
|
||||
|
||||
Wrapper = () => {
|
||||
return mount(VerifyNonce, {
|
||||
return mount(EnterNonce, {
|
||||
mocks,
|
||||
localVue,
|
||||
propsData,
|
||||
})
|
||||
}
|
||||
|
||||
it('renders a verify nonce form', () => {
|
||||
it('renders an enter nonce form', () => {
|
||||
wrapper = Wrapper()
|
||||
expect(wrapper.find('.verify-nonce').exists()).toBe(true)
|
||||
expect(wrapper.find('form').exists()).toBe(true)
|
||||
})
|
||||
|
||||
describe('after verification nonce given', () => {
|
||||
describe('after nonce entered', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
wrapper.find('input#nonce').setValue('123456')
|
||||
wrapper.find('form').trigger('submit')
|
||||
})
|
||||
|
||||
it('emits `verification`', () => {
|
||||
it('emits `nonceEntered`', () => {
|
||||
const expected = [[{ nonce: '123456', email: 'mail@example.org' }]]
|
||||
expect(wrapper.emitted('verification')).toEqual(expected)
|
||||
expect(wrapper.emitted('nonceEntered')).toEqual(expected)
|
||||
})
|
||||
})
|
||||
})
|
||||
66
webapp/components/EnterNonce/EnterNonce.vue
Normal file
66
webapp/components/EnterNonce/EnterNonce.vue
Normal file
@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<ds-space margin-top="large" margin-bottom="xxx-small">
|
||||
<ds-form
|
||||
v-model="formData"
|
||||
:schema="formSchema"
|
||||
@submit="handleSubmitVerify"
|
||||
@input="handleInput"
|
||||
@input-valid="handleInputValid"
|
||||
>
|
||||
<ds-input
|
||||
:placeholder="$t('components.enter-nonce.form.nonce')"
|
||||
model="nonce"
|
||||
name="nonce"
|
||||
id="nonce"
|
||||
icon="question-circle"
|
||||
/>
|
||||
<ds-space margin-botton="large">
|
||||
<ds-text>
|
||||
{{ $t('components.enter-nonce.form.description') }}
|
||||
</ds-text>
|
||||
</ds-space>
|
||||
<ds-button :disabled="disabled" primary fullwidth name="submit" type="submit">
|
||||
{{ $t('components.enter-nonce.form.next') }}
|
||||
</ds-button>
|
||||
</ds-form>
|
||||
<slot></slot>
|
||||
</ds-space>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
email: { type: String, required: true },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
formData: {
|
||||
nonce: '',
|
||||
},
|
||||
formSchema: {
|
||||
nonce: {
|
||||
type: 'string',
|
||||
min: 6,
|
||||
max: 6,
|
||||
required: true,
|
||||
message: this.$t('components.enter-nonce.form.validations.length'),
|
||||
},
|
||||
},
|
||||
disabled: true,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async handleInput() {
|
||||
this.disabled = true
|
||||
},
|
||||
async handleInputValid() {
|
||||
this.disabled = false
|
||||
},
|
||||
handleSubmitVerify() {
|
||||
const { nonce } = this.formData
|
||||
const email = this.email
|
||||
this.$emit('nonceEntered', { email, nonce })
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -39,7 +39,7 @@ describe('ChangePassword ', () => {
|
||||
})
|
||||
}
|
||||
|
||||
describe('given email and verification nonce', () => {
|
||||
describe('given email and nonce', () => {
|
||||
beforeEach(() => {
|
||||
propsData.email = 'mail@example.org'
|
||||
propsData.nonce = '123456'
|
||||
@ -66,7 +66,7 @@ describe('ChangePassword ', () => {
|
||||
|
||||
describe('password reset successful', () => {
|
||||
it('displays success message', () => {
|
||||
const expected = 'verify-nonce.form.change-password.success'
|
||||
const expected = 'components.password-reset.change-password.success'
|
||||
expect(mocks.$t).toHaveBeenCalledWith(expected)
|
||||
})
|
||||
|
||||
|
||||
@ -1,54 +1,62 @@
|
||||
<template>
|
||||
<ds-card class="verify-nonce">
|
||||
<ds-space margin="large">
|
||||
<ds-form
|
||||
v-if="!changePasswordResult"
|
||||
v-model="formData"
|
||||
:schema="formSchema"
|
||||
@submit="handleSubmitPassword"
|
||||
class="change-password"
|
||||
>
|
||||
<template slot-scope="{ errors }">
|
||||
<ds-input
|
||||
id="password"
|
||||
model="password"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
:label="$t('settings.security.change-password.label-new-password')"
|
||||
/>
|
||||
<ds-input
|
||||
id="passwordConfirmation"
|
||||
model="passwordConfirmation"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
:label="$t('settings.security.change-password.label-new-password-confirm')"
|
||||
/>
|
||||
<password-strength :password="formData.password" />
|
||||
<ds-space margin-top="base">
|
||||
<ds-button :loading="$apollo.loading" :disabled="errors" primary>
|
||||
{{ $t('settings.security.change-password.button') }}
|
||||
</ds-button>
|
||||
</ds-space>
|
||||
</template>
|
||||
</ds-form>
|
||||
<ds-text v-else>
|
||||
<template v-if="changePasswordResult === 'success'">
|
||||
<ds-space margin-top="base" margin-bottom="xxx-small">
|
||||
<ds-form
|
||||
v-if="!changePasswordResult"
|
||||
v-model="formData"
|
||||
:schema="formSchema"
|
||||
@submit="handleSubmitPassword"
|
||||
class="change-password"
|
||||
>
|
||||
<template slot-scope="{ errors }">
|
||||
<ds-input
|
||||
id="password"
|
||||
model="password"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
:label="$t('settings.security.change-password.label-new-password')"
|
||||
/>
|
||||
<ds-input
|
||||
id="passwordConfirmation"
|
||||
model="passwordConfirmation"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
:label="$t('settings.security.change-password.label-new-password-confirm')"
|
||||
/>
|
||||
<password-strength :password="formData.password" />
|
||||
<ds-space margin-top="base" margin-bottom="xxx-small">
|
||||
<ds-button :loading="$apollo.loading" :disabled="errors" primary>
|
||||
{{ $t('settings.security.change-password.button') }}
|
||||
</ds-button>
|
||||
</ds-space>
|
||||
</template>
|
||||
</ds-form>
|
||||
<ds-space v-else>
|
||||
<template v-if="changePasswordResult === 'success'">
|
||||
<transition name="ds-transition-fade">
|
||||
<sweetalert-icon icon="success" />
|
||||
<ds-text>
|
||||
{{ $t(`verify-nonce.form.change-password.success`) }}
|
||||
</ds-text>
|
||||
</template>
|
||||
<template v-else>
|
||||
</transition>
|
||||
<ds-text>
|
||||
{{ $t('components.password-reset.change-password.success') }}
|
||||
</ds-text>
|
||||
</template>
|
||||
<template v-else>
|
||||
<transition name="ds-transition-fade">
|
||||
<sweetalert-icon icon="error" />
|
||||
<ds-text align="left">
|
||||
{{ $t(`verify-nonce.form.change-password.error`) }}
|
||||
{{ $t('verify-nonce.form.change-password.help') }}
|
||||
</ds-text>
|
||||
<a href="mailto:support@human-connection.org">support@human-connection.org</a>
|
||||
</template>
|
||||
</ds-text>
|
||||
</transition>
|
||||
<ds-text>
|
||||
<p>
|
||||
{{ $t(`components.password-reset.change-password.error`) }}
|
||||
</p>
|
||||
<p>
|
||||
{{ $t('components.password-reset.change-password.help') }}
|
||||
<br />
|
||||
<a href="mailto:support@human-connection.org">support@human-connection.org</a>
|
||||
</p>
|
||||
</ds-text>
|
||||
</template>
|
||||
<slot></slot>
|
||||
</ds-space>
|
||||
</ds-card>
|
||||
</ds-space>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
@ -18,7 +18,7 @@ describe('Request', () => {
|
||||
success: jest.fn(),
|
||||
error: jest.fn(),
|
||||
},
|
||||
$t: jest.fn(),
|
||||
$t: jest.fn(t => t),
|
||||
$apollo: {
|
||||
loading: false,
|
||||
mutate: jest.fn().mockResolvedValue({ data: { reqestPasswordReset: true } }),
|
||||
@ -45,7 +45,7 @@ describe('Request', () => {
|
||||
|
||||
it('renders a password reset form', () => {
|
||||
wrapper = Wrapper()
|
||||
expect(wrapper.find('.password-reset').exists()).toBe(true)
|
||||
expect(wrapper.find('form').exists()).toBe(true)
|
||||
})
|
||||
|
||||
describe('submit', () => {
|
||||
@ -69,7 +69,10 @@ describe('Request', () => {
|
||||
})
|
||||
|
||||
it('displays a message that a password email was requested', () => {
|
||||
const expected = ['password-reset.form.submitted', { email: 'mail@example.org' }]
|
||||
const expected = [
|
||||
'components.password-reset.request.form.submitted',
|
||||
{ email: 'mail@example.org' },
|
||||
]
|
||||
expect(mocks.$t).toHaveBeenCalledWith(...expected)
|
||||
})
|
||||
|
||||
|
||||
@ -1,77 +1,55 @@
|
||||
<template>
|
||||
<ds-card class="password-reset">
|
||||
<ds-flex gutter="small">
|
||||
<ds-flex-item :width="{ base: '100%', sm: '50%' }" centered>
|
||||
<client-only>
|
||||
<locale-switch class="login-locale-switch" offset="5" />
|
||||
</client-only>
|
||||
<ds-space margin-top="small" margin-bottom="xxx-small" centered>
|
||||
<img class="login-image" alt="Human Connection" src="/img/sign-up/humanconnection.svg" />
|
||||
</ds-space>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item :width="{ base: '100%', sm: '50%' }" centered>
|
||||
<ds-space margin="small">
|
||||
<ds-text size="small" align="left">{{ $t('login.copy') }}</ds-text>
|
||||
</ds-space>
|
||||
<ds-form
|
||||
v-if="!submitted"
|
||||
@input="handleInput"
|
||||
@input-valid="handleInputValid"
|
||||
v-model="formData"
|
||||
:schema="formSchema"
|
||||
@submit="handleSubmit"
|
||||
>
|
||||
<ds-input
|
||||
:placeholder="$t('login.email')"
|
||||
type="email"
|
||||
id="email"
|
||||
model="email"
|
||||
name="email"
|
||||
icon="envelope"
|
||||
/>
|
||||
<ds-space margin-botton="large">
|
||||
<ds-text align="left">{{ $t('password-reset.form.description') }}</ds-text>
|
||||
</ds-space>
|
||||
<ds-button
|
||||
:disabled="disabled"
|
||||
:loading="$apollo.loading"
|
||||
primary
|
||||
fullwidth
|
||||
name="submit"
|
||||
type="submit"
|
||||
icon="envelope"
|
||||
>
|
||||
{{ $t('password-reset.form.submit') }}
|
||||
</ds-button>
|
||||
</ds-form>
|
||||
<div v-else>
|
||||
<transition name="ds-transition-fade">
|
||||
<ds-flex centered>
|
||||
<sweetalert-icon icon="info" />
|
||||
</ds-flex>
|
||||
</transition>
|
||||
<ds-text v-html="submitMessage" align="left" />
|
||||
</div>
|
||||
<ds-space margin-bottom="small" />
|
||||
<div>
|
||||
<nuxt-link to="/login">{{ $t('site.login') }}</nuxt-link>
|
||||
</div>
|
||||
</ds-flex-item>
|
||||
</ds-flex>
|
||||
|
||||
<ds-space margin="x-small"></ds-space>
|
||||
</ds-card>
|
||||
<ds-form
|
||||
v-if="!submitted"
|
||||
@input="handleInput"
|
||||
@input-valid="handleInputValid"
|
||||
v-model="formData"
|
||||
:schema="formSchema"
|
||||
@submit="handleSubmit"
|
||||
>
|
||||
<ds-space margin="small">
|
||||
<ds-input
|
||||
:placeholder="$t('login.email')"
|
||||
type="email"
|
||||
id="email"
|
||||
model="email"
|
||||
name="email"
|
||||
icon="envelope"
|
||||
/>
|
||||
</ds-space>
|
||||
<ds-space margin-botton="large">
|
||||
<ds-text align="left">{{ $t('components.password-reset.request.form.description') }}</ds-text>
|
||||
</ds-space>
|
||||
<ds-button
|
||||
:disabled="disabled"
|
||||
:loading="$apollo.loading"
|
||||
primary
|
||||
fullwidth
|
||||
name="submit"
|
||||
type="submit"
|
||||
icon="envelope"
|
||||
>
|
||||
{{ $t('components.password-reset.request.form.submit') }}
|
||||
</ds-button>
|
||||
<slot></slot>
|
||||
</ds-form>
|
||||
<div v-else>
|
||||
<transition name="ds-transition-fade">
|
||||
<ds-flex centered>
|
||||
<sweetalert-icon icon="info" />
|
||||
</ds-flex>
|
||||
</transition>
|
||||
<ds-text v-html="submitMessage" align="left" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import gql from 'graphql-tag'
|
||||
import { SweetalertIcon } from 'vue-sweetalert-icons'
|
||||
import LocaleSwitch from '../LocaleSwitch/LocaleSwitch'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SweetalertIcon,
|
||||
LocaleSwitch,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@ -92,7 +70,7 @@ export default {
|
||||
computed: {
|
||||
submitMessage() {
|
||||
const { email } = this.formData
|
||||
return this.$t('password-reset.form.submitted', { email })
|
||||
return this.$t('components.password-reset.request.form.submitted', { email })
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
@ -124,17 +102,3 @@ export default {
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.login-image {
|
||||
width: 90%;
|
||||
max-width: 200px;
|
||||
}
|
||||
.password-reset {
|
||||
position: relative;
|
||||
}
|
||||
.login-locale-switch {
|
||||
position: absolute;
|
||||
top: 1em;
|
||||
left: 1em;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,67 +0,0 @@
|
||||
<template>
|
||||
<ds-card class="verify-nonce">
|
||||
<ds-space margin="large">
|
||||
<ds-form
|
||||
v-model="formData"
|
||||
:schema="formSchema"
|
||||
@submit="handleSubmitVerify"
|
||||
@input="handleInput"
|
||||
@input-valid="handleInputValid"
|
||||
>
|
||||
<ds-input
|
||||
:placeholder="$t('verify-nonce.form.nonce')"
|
||||
model="nonce"
|
||||
name="nonce"
|
||||
id="nonce"
|
||||
icon="question-circle"
|
||||
/>
|
||||
<ds-space margin-botton="large">
|
||||
<ds-text>
|
||||
{{ $t('verify-nonce.form.description') }}
|
||||
</ds-text>
|
||||
</ds-space>
|
||||
<ds-button :disabled="disabled" primary fullwidth name="submit" type="submit">
|
||||
{{ $t('verify-nonce.form.next') }}
|
||||
</ds-button>
|
||||
</ds-form>
|
||||
</ds-space>
|
||||
</ds-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
email: { type: String, required: true },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
formData: {
|
||||
nonce: '',
|
||||
},
|
||||
formSchema: {
|
||||
nonce: {
|
||||
type: 'string',
|
||||
min: 6,
|
||||
max: 6,
|
||||
required: true,
|
||||
message: this.$t('common.validations.verification-nonce'),
|
||||
},
|
||||
},
|
||||
disabled: true,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async handleInput() {
|
||||
this.disabled = true
|
||||
},
|
||||
async handleInputValid() {
|
||||
this.disabled = false
|
||||
},
|
||||
handleSubmitVerify() {
|
||||
const { nonce } = this.formData
|
||||
const email = this.email
|
||||
this.$emit('verification', { email, nonce })
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -8,6 +8,7 @@ const localVue = createLocalVue()
|
||||
localVue.use(Styleguide)
|
||||
config.stubs['sweetalert-icon'] = '<span><slot /></span>'
|
||||
config.stubs['client-only'] = '<span><slot /></span>'
|
||||
config.stubs['nuxt-link'] = '<span><slot /></span>'
|
||||
|
||||
describe('CreateUserAccount', () => {
|
||||
let wrapper, Wrapper, mocks, propsData, stubs
|
||||
@ -102,7 +103,9 @@ describe('CreateUserAccount', () => {
|
||||
|
||||
it('displays success', async () => {
|
||||
await action()
|
||||
expect(mocks.$t).toHaveBeenCalledWith('registration.create-user-account.success')
|
||||
expect(mocks.$t).toHaveBeenCalledWith(
|
||||
'components.registration.create-user-account.success',
|
||||
)
|
||||
})
|
||||
|
||||
describe('after timeout', () => {
|
||||
@ -130,7 +133,9 @@ describe('CreateUserAccount', () => {
|
||||
|
||||
it('displays form errors', async () => {
|
||||
await action()
|
||||
expect(wrapper.find('.backendErrors').text()).toContain('Invalid nonce')
|
||||
expect(mocks.$t).toHaveBeenCalledWith(
|
||||
'components.registration.create-user-account.error',
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,117 +1,111 @@
|
||||
<template>
|
||||
<ds-container width="small">
|
||||
<ds-card v-if="success" class="success">
|
||||
<ds-space>
|
||||
<sweetalert-icon icon="success" />
|
||||
<ds-text align="center" bold color="success">
|
||||
{{ $t('registration.create-user-account.success') }}
|
||||
</ds-text>
|
||||
</ds-space>
|
||||
</ds-card>
|
||||
<ds-card v-else class="create-account-card">
|
||||
<client-only>
|
||||
<locale-switch />
|
||||
</client-only>
|
||||
<ds-space centered>
|
||||
<img
|
||||
class="create-account-image"
|
||||
alt="Create an account for Human Connection"
|
||||
src="/img/sign-up/nicetomeetyou.svg"
|
||||
<div v-if="response === 'success'">
|
||||
<transition name="ds-transition-fade">
|
||||
<sweetalert-icon icon="success" />
|
||||
</transition>
|
||||
<ds-text align="center" bold color="success">
|
||||
{{ $t('components.registration.create-user-account.success') }}
|
||||
</ds-text>
|
||||
</div>
|
||||
<div v-else-if="response === 'error'">
|
||||
<transition name="ds-transition-fade">
|
||||
<sweetalert-icon icon="error" />
|
||||
</transition>
|
||||
<ds-text align="center" bold color="danger">
|
||||
{{ $t('components.registration.create-user-account.error') }}
|
||||
</ds-text>
|
||||
<ds-text align="center">
|
||||
{{ $t('components.registration.create-user-account.help') }}
|
||||
<a :href="supportEmail.href">{{ supportEmail.label }}</a>
|
||||
</ds-text>
|
||||
<ds-space centered>
|
||||
<nuxt-link to="/login">{{ $t('site.back-to-login') }}</nuxt-link>
|
||||
</ds-space>
|
||||
</div>
|
||||
<div v-else class="create-account-card">
|
||||
<ds-space margin-top="large">
|
||||
<ds-heading size="h3">
|
||||
{{ $t('components.registration.create-user-account.title') }}
|
||||
</ds-heading>
|
||||
</ds-space>
|
||||
|
||||
<ds-form class="create-user-account" v-model="formData" :schema="formSchema" @submit="submit">
|
||||
<template v-slot="{ errors }">
|
||||
<ds-input
|
||||
id="name"
|
||||
model="name"
|
||||
icon="user"
|
||||
:label="$t('settings.data.labelName')"
|
||||
:placeholder="$t('settings.data.namePlaceholder')"
|
||||
/>
|
||||
</ds-space>
|
||||
<ds-space>
|
||||
<ds-heading size="h3">
|
||||
{{ $t('registration.create-user-account.title') }}
|
||||
</ds-heading>
|
||||
</ds-space>
|
||||
<ds-input
|
||||
id="about"
|
||||
model="about"
|
||||
type="textarea"
|
||||
rows="3"
|
||||
:label="$t('settings.data.labelBio')"
|
||||
:placeholder="$t('settings.data.labelBio')"
|
||||
/>
|
||||
<ds-input
|
||||
id="password"
|
||||
model="password"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
:label="$t('settings.security.change-password.label-new-password')"
|
||||
/>
|
||||
<ds-input
|
||||
id="passwordConfirmation"
|
||||
model="passwordConfirmation"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
:label="$t('settings.security.change-password.label-new-password-confirm')"
|
||||
/>
|
||||
<password-strength :password="formData.password" />
|
||||
|
||||
<ds-form class="create-user-account" v-model="formData" :schema="formSchema" @submit="submit">
|
||||
<template v-slot="{ errors }">
|
||||
<ds-flex gutter="base">
|
||||
<ds-flex-item width="100%">
|
||||
<ds-input
|
||||
id="name"
|
||||
model="name"
|
||||
icon="user"
|
||||
:label="$t('settings.data.labelName')"
|
||||
:placeholder="$t('settings.data.namePlaceholder')"
|
||||
/>
|
||||
<ds-input
|
||||
id="about"
|
||||
model="about"
|
||||
type="textarea"
|
||||
rows="3"
|
||||
:label="$t('settings.data.labelBio')"
|
||||
:placeholder="$t('settings.data.labelBio')"
|
||||
/>
|
||||
<ds-input
|
||||
id="password"
|
||||
model="password"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
:label="$t('settings.security.change-password.label-new-password')"
|
||||
/>
|
||||
<ds-input
|
||||
id="passwordConfirmation"
|
||||
model="passwordConfirmation"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
:label="$t('settings.security.change-password.label-new-password-confirm')"
|
||||
/>
|
||||
<password-strength :password="formData.password" />
|
||||
|
||||
<ds-text>
|
||||
<input
|
||||
id="checkbox"
|
||||
type="checkbox"
|
||||
v-model="termsAndConditionsConfirmed"
|
||||
:checked="termsAndConditionsConfirmed"
|
||||
/>
|
||||
<label
|
||||
for="checkbox"
|
||||
v-html="$t('termsAndConditions.termsAndConditionsConfirmed')"
|
||||
></label>
|
||||
</ds-text>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item width="100%">
|
||||
<ds-space class="backendErrors" v-if="backendErrors">
|
||||
<ds-text align="center" bold color="danger">{{ backendErrors.message }}</ds-text>
|
||||
</ds-space>
|
||||
<ds-button
|
||||
style="float: right;"
|
||||
icon="check"
|
||||
type="submit"
|
||||
:loading="$apollo.loading"
|
||||
:disabled="errors || !termsAndConditionsConfirmed"
|
||||
primary
|
||||
>
|
||||
{{ $t('actions.save') }}
|
||||
</ds-button>
|
||||
</ds-flex-item>
|
||||
</ds-flex>
|
||||
</template>
|
||||
</ds-form>
|
||||
</ds-card>
|
||||
</ds-container>
|
||||
<ds-text>
|
||||
<input
|
||||
id="checkbox"
|
||||
type="checkbox"
|
||||
v-model="termsAndConditionsConfirmed"
|
||||
:checked="termsAndConditionsConfirmed"
|
||||
/>
|
||||
<label
|
||||
for="checkbox"
|
||||
v-html="$t('termsAndConditions.termsAndConditionsConfirmed')"
|
||||
></label>
|
||||
</ds-text>
|
||||
<ds-button
|
||||
style="float: right;"
|
||||
icon="check"
|
||||
type="submit"
|
||||
:loading="$apollo.loading"
|
||||
:disabled="errors || !termsAndConditionsConfirmed"
|
||||
primary
|
||||
>
|
||||
{{ $t('actions.save') }}
|
||||
</ds-button>
|
||||
</template>
|
||||
</ds-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PasswordStrength from '../Password/Strength'
|
||||
import { SweetalertIcon } from 'vue-sweetalert-icons'
|
||||
import PasswordForm from '~/components/utils/PasswordFormHelper'
|
||||
import { SUPPORT_EMAIL } from '~/constants/emails.js'
|
||||
import { VERSION } from '~/constants/terms-and-conditions-version.js'
|
||||
import { SignupVerificationMutation } from '~/graphql/Registration.js'
|
||||
import LocaleSwitch from '~/components/LocaleSwitch/LocaleSwitch'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PasswordStrength,
|
||||
SweetalertIcon,
|
||||
LocaleSwitch,
|
||||
},
|
||||
data() {
|
||||
const passwordForm = PasswordForm({ translate: this.$t })
|
||||
return {
|
||||
supportEmail: SUPPORT_EMAIL,
|
||||
formData: {
|
||||
name: '',
|
||||
about: '',
|
||||
@ -130,8 +124,7 @@ export default {
|
||||
...passwordForm.formSchema,
|
||||
},
|
||||
disabled: true,
|
||||
success: null,
|
||||
backendErrors: null,
|
||||
response: null,
|
||||
// TODO: Our styleguide does not support checkmarks.
|
||||
// Integrate termsAndConditionsConfirmed into `this.formData` once we
|
||||
// have checkmarks available.
|
||||
@ -152,7 +145,7 @@ export default {
|
||||
mutation: SignupVerificationMutation,
|
||||
variables: { name, password, about, email, nonce, termsAndConditionsAgreedVersion },
|
||||
})
|
||||
this.success = true
|
||||
this.response = 'success'
|
||||
setTimeout(() => {
|
||||
this.$emit('userCreated', {
|
||||
email,
|
||||
@ -160,7 +153,7 @@ export default {
|
||||
})
|
||||
}, 3000)
|
||||
} catch (err) {
|
||||
this.backendErrors = err
|
||||
this.response = 'error'
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@ -42,7 +42,7 @@ describe('Signup', () => {
|
||||
describe('without invitation code', () => {
|
||||
it('renders signup form', () => {
|
||||
wrapper = Wrapper()
|
||||
expect(wrapper.find('.signup').exists()).toBe(true)
|
||||
expect(wrapper.find('form').exists()).toBe(true)
|
||||
})
|
||||
|
||||
describe('submit', () => {
|
||||
@ -69,15 +69,18 @@ describe('Signup', () => {
|
||||
})
|
||||
|
||||
it('displays a message that a mail for email verification was sent', () => {
|
||||
const expected = ['registration.signup.form.success', { email: 'mail@example.org' }]
|
||||
const expected = [
|
||||
'components.registration.signup.form.success',
|
||||
{ email: 'mail@example.org' },
|
||||
]
|
||||
expect(mocks.$t).toHaveBeenCalledWith(...expected)
|
||||
})
|
||||
|
||||
describe('after animation', () => {
|
||||
beforeEach(jest.runAllTimers)
|
||||
|
||||
it('emits `handleSubmitted`', () => {
|
||||
expect(wrapper.emitted('handleSubmitted')).toEqual([[{ email: 'mail@example.org' }]])
|
||||
it('emits `submit`', () => {
|
||||
expect(wrapper.emitted('submit')).toEqual([[{ email: 'mail@example.org' }]])
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -121,7 +124,9 @@ describe('Signup', () => {
|
||||
|
||||
it('explains the error', async () => {
|
||||
await action()
|
||||
expect(mocks.$t).toHaveBeenCalledWith('registration.signup.form.errors.email-exists')
|
||||
expect(mocks.$t).toHaveBeenCalledWith(
|
||||
'components.registration.signup.form.errors.email-exists',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -137,7 +142,7 @@ describe('Signup', () => {
|
||||
it('explains the error', async () => {
|
||||
await action()
|
||||
expect(mocks.$t).toHaveBeenCalledWith(
|
||||
'registration.signup.form.errors.invalid-invitation-token',
|
||||
'components.registration.signup.form.errors.invalid-invitation-token',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,59 +1,63 @@
|
||||
<template>
|
||||
<ds-card class="signup">
|
||||
<ds-space margin="large">
|
||||
<ds-form
|
||||
v-if="!success && !error"
|
||||
@input="handleInput"
|
||||
@input-valid="handleInputValid"
|
||||
v-model="formData"
|
||||
:schema="formSchema"
|
||||
@submit="handleSubmit"
|
||||
<ds-space v-if="!success && !error" margin="large">
|
||||
<ds-form
|
||||
@input="handleInput"
|
||||
@input-valid="handleInputValid"
|
||||
v-model="formData"
|
||||
:schema="formSchema"
|
||||
@submit="handleSubmit"
|
||||
>
|
||||
<h1>
|
||||
{{ invitation ? $t('profile.invites.title') : $t('components.registration.signup.title') }}
|
||||
</h1>
|
||||
<ds-space v-if="token" margin-botton="large">
|
||||
<ds-text v-html="$t('registration.signup.form.invitation-code', { code: token })" />
|
||||
</ds-space>
|
||||
<ds-space margin-botton="large">
|
||||
<ds-text>
|
||||
{{
|
||||
invitation
|
||||
? $t('profile.invites.description')
|
||||
: $t('components.registration.signup.form.description')
|
||||
}}
|
||||
</ds-text>
|
||||
</ds-space>
|
||||
<ds-input
|
||||
:placeholder="invitation ? $t('profile.invites.emailPlaceholder') : $t('login.email')"
|
||||
type="email"
|
||||
id="email"
|
||||
model="email"
|
||||
name="email"
|
||||
icon="envelope"
|
||||
/>
|
||||
<ds-button
|
||||
:disabled="disabled"
|
||||
:loading="$apollo.loading"
|
||||
primary
|
||||
fullwidth
|
||||
name="submit"
|
||||
type="submit"
|
||||
icon="envelope"
|
||||
>
|
||||
<h1>{{ invitation ? $t('profile.invites.title') : $t('registration.signup.title') }}</h1>
|
||||
<ds-space v-if="token" margin-botton="large">
|
||||
<ds-text v-html="$t('registration.signup.form.invitation-code', { code: token })" />
|
||||
</ds-space>
|
||||
<ds-space margin-botton="large">
|
||||
<ds-text>
|
||||
{{
|
||||
invitation
|
||||
? $t('profile.invites.description')
|
||||
: $t('registration.signup.form.description')
|
||||
}}
|
||||
</ds-text>
|
||||
</ds-space>
|
||||
<ds-input
|
||||
:placeholder="invitation ? $t('profile.invites.emailPlaceholder') : $t('login.email')"
|
||||
type="email"
|
||||
id="email"
|
||||
model="email"
|
||||
name="email"
|
||||
icon="envelope"
|
||||
/>
|
||||
<ds-button
|
||||
:disabled="disabled"
|
||||
:loading="$apollo.loading"
|
||||
primary
|
||||
fullwidth
|
||||
name="submit"
|
||||
type="submit"
|
||||
icon="envelope"
|
||||
>
|
||||
{{ $t('registration.signup.form.submit') }}
|
||||
</ds-button>
|
||||
</ds-form>
|
||||
<div v-else>
|
||||
<template v-if="!error">
|
||||
<sweetalert-icon icon="info" />
|
||||
<ds-text align="center" v-html="submitMessage" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<sweetalert-icon icon="error" />
|
||||
<ds-text align="center">{{ error.message }}</ds-text>
|
||||
</template>
|
||||
</div>
|
||||
</ds-space>
|
||||
</ds-card>
|
||||
{{ $t('components.registration.signup.form.submit') }}
|
||||
</ds-button>
|
||||
<slot></slot>
|
||||
</ds-form>
|
||||
</ds-space>
|
||||
<div v-else margin="large">
|
||||
<template v-if="!error">
|
||||
<transition name="ds-transition-fade">
|
||||
<sweetalert-icon icon="info" />
|
||||
</transition>
|
||||
<ds-text align="center" v-html="submitMessage" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<transition name="ds-transition-fade">
|
||||
<sweetalert-icon icon="error" />
|
||||
</transition>
|
||||
<ds-text align="center">{{ error.message }}</ds-text>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -103,7 +107,7 @@ export default {
|
||||
computed: {
|
||||
submitMessage() {
|
||||
const { email } = this.formData
|
||||
return this.$t('registration.signup.form.success', { email })
|
||||
return this.$t('components.registration.signup.form.success', { email })
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
@ -123,7 +127,7 @@ export default {
|
||||
this.success = true
|
||||
|
||||
setTimeout(() => {
|
||||
this.$emit('handleSubmitted', { email })
|
||||
this.$emit('submit', { email })
|
||||
}, 3000)
|
||||
} catch (err) {
|
||||
const { message } = err
|
||||
@ -133,7 +137,10 @@ export default {
|
||||
}
|
||||
for (const [pattern, key] of Object.entries(mapping)) {
|
||||
if (message.includes(pattern))
|
||||
this.error = { key, message: this.$t(`registration.signup.form.errors.${key}`) }
|
||||
this.error = {
|
||||
key,
|
||||
message: this.$t(`components.registration.signup.form.errors.${key}`),
|
||||
}
|
||||
}
|
||||
if (!this.error) {
|
||||
this.$toast.error(message)
|
||||
|
||||
4
webapp/constants/emails.js
Normal file
4
webapp/constants/emails.js
Normal file
@ -0,0 +1,4 @@
|
||||
export const SUPPORT_EMAIL = {
|
||||
href: 'mailto:support@human-connection.org',
|
||||
label: 'support@human-connection.org',
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
@ -1,4 +1,52 @@
|
||||
{
|
||||
"components": {
|
||||
"password-reset": {
|
||||
"request": {
|
||||
"form": {
|
||||
"description": "Eine Mail zum Zurücksetzen des Passworts wird an die angegebene E-Mail Adresse geschickt.",
|
||||
"submit": "Email anfordern",
|
||||
"submitted": "Eine E-Mail mit weiteren Instruktionen wurde verschickt an <b>{email}</b>"
|
||||
}
|
||||
},
|
||||
"change-password": {
|
||||
"success": "Änderung des Passworts war erfolgreich!",
|
||||
"error": "Passwort Änderung fehlgeschlagen. Möglicherweise falscher Sicherheitscode?",
|
||||
"help": "Falls Probleme auftreten, schreib uns gerne eine Mail an:"
|
||||
}
|
||||
},
|
||||
"enter-nonce": {
|
||||
"form": {
|
||||
"nonce": "Code eingeben",
|
||||
"description": "Öffne dein E-Mail Postfach und gib den Code ein, den wir geschickt haben.",
|
||||
"next": "Weiter",
|
||||
"validations": {
|
||||
"length": "muss genau 6 Buchstaben lang sein"
|
||||
}
|
||||
}
|
||||
},
|
||||
"registration": {
|
||||
"signup": {
|
||||
"unavailable": "Leider ist die öffentliche Registrierung von Benutzerkonten auf diesem Server derzeit nicht möglich.",
|
||||
"title": "Mach mit bei Human Connection!",
|
||||
"form": {
|
||||
"description": "Um loszulegen, gib deine E-Mail Adresse ein:",
|
||||
"invitation-code": "Dein Einladungscode lautet: <b>{code}</b>",
|
||||
"errors": {
|
||||
"email-exists": "Es gibt schon ein Benutzerkonto mit dieser E-Mail Adresse!",
|
||||
"invalid-invitation-token": "Es sieht so aus, als ob der Einladungscode schon eingelöst wurde. Jeder Code kann nur einmalig benutzt werden."
|
||||
},
|
||||
"submit": "Konto erstellen",
|
||||
"success": "Eine Mail mit einem Bestätigungslink für die Registrierung wurde an <b>{email}</b> geschickt"
|
||||
}
|
||||
},
|
||||
"create-user-account": {
|
||||
"title": "Benutzerkonto anlegen",
|
||||
"success": "Dein Benutzerkonto wurde erstellt!",
|
||||
"error": "Es konnte kein Benutzerkonto erstellt werden!",
|
||||
"help": "Vielleicht war der Bestätigungscode falsch oder abgelaufen? Wenn das Problem weiterhin besteht, schick uns gerne eine E-Mail an:"
|
||||
}
|
||||
}
|
||||
},
|
||||
"maintenance": {
|
||||
"title": "Human Connection befindet sich in der Wartung",
|
||||
"explanation": "Zurzeit führen wir einige geplante Wartungsarbeiten durch, bitte versuch es später erneut.",
|
||||
@ -42,7 +90,7 @@
|
||||
"bank": "Bankverbindung",
|
||||
"germany": "Deutschland",
|
||||
"code-of-conduct": "Verhaltenscodex",
|
||||
"login": "Zurück zum Anmeldung"
|
||||
"back-to-login": "Zurück zur Anmeldung"
|
||||
},
|
||||
"sorting": {
|
||||
"newest": "Neueste",
|
||||
@ -55,51 +103,14 @@
|
||||
"email": "Deine E-Mail",
|
||||
"password": "Dein Passwort",
|
||||
"forgotPassword": "Passwort vergessen?",
|
||||
"no-account": "Du hast noch keinen Benutzerkonto?",
|
||||
"register": "Benutzerkonto erstellen",
|
||||
"moreInfo": "Was ist Human Connection?",
|
||||
"moreInfoURL": "https://human-connection.org",
|
||||
"moreInfoHint": "zur Präsentationsseite",
|
||||
"hello": "Hallo",
|
||||
"success": "Du bist eingeloggt!"
|
||||
},
|
||||
"password-reset": {
|
||||
"title": "Dein Passwort zurücksetzen",
|
||||
"form": {
|
||||
"description": "Eine Mail zum Zurücksetzen des Passworts wird an die angegebene E-Mail Adresse geschickt.",
|
||||
"submit": "Email anfordern",
|
||||
"submitted": "Eine E-Mail mit weiteren Instruktionen wurde verschickt an <b>{email}</b>"
|
||||
}
|
||||
},
|
||||
"registration": {
|
||||
"signup": {
|
||||
"title": "Mach mit bei Human Connection!",
|
||||
"form": {
|
||||
"description": "Um loszulegen, gib deine E-Mail Adresse ein:",
|
||||
"invitation-code": "Dein Einladungscode lautet: <b>{code}</b>",
|
||||
"errors": {
|
||||
"email-exists": "Es gibt schon ein Benutzerkonto mit dieser E-Mail Adresse!",
|
||||
"invalid-invitation-token": "Es sieht so aus, als ob der Einladungscode schon eingelöst wurde. Jeder Code kann nur einmalig benutzt werden."
|
||||
},
|
||||
"submit": "Konto erstellen",
|
||||
"success": "Eine Mail mit einem Bestätigungslink für die Registrierung wurde an <b>{email}</b> geschickt"
|
||||
}
|
||||
},
|
||||
"create-user-account": {
|
||||
"title": "Benutzerkonto anlegen",
|
||||
"success": "Dein Benutzerkonto wurde erstellt!"
|
||||
}
|
||||
},
|
||||
"verify-nonce": {
|
||||
"form": {
|
||||
"nonce": "Code eingeben",
|
||||
"description": "Öffne dein E-Mail Postfach und gib den Code ein, den wir geschickt haben.",
|
||||
"next": "Weiter",
|
||||
"change-password": {
|
||||
"success": "Änderung des Passworts war erfolgreich!",
|
||||
"error": "Passwort Änderung fehlgeschlagen. Möglicherweise falscher Sicherheitscode?",
|
||||
"help": "Falls Probleme auftreten, schreib uns gerne eine Mail an:"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"placeholder": "Schreib etwas Inspirierendes …",
|
||||
"mention": {
|
||||
@ -109,6 +120,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": {
|
||||
@ -221,6 +238,22 @@
|
||||
"success": "Konto erfolgreich gelöscht!",
|
||||
"pleaseConfirm": "<b class='is-danger'>Zerstörerische Aktion!</b> Gib <b>{confirm}</b> ein, um zu bestätigen."
|
||||
},
|
||||
"embeds": {
|
||||
"name": "Drittanbieter",
|
||||
"info-description": "Hier ist die Liste an Drittanbietern, deren Inhalte als Fremdcode z.B. in Form von eingebetteten Videos angezeigt werden kann:",
|
||||
"status": {
|
||||
"description": "Als Grundeinstellung für dich wird eingebetter Fremdcode von Drittanbietern",
|
||||
"disabled": {
|
||||
"false": "zunächst nicht angezeigt",
|
||||
"true": "sofort angezeigt"
|
||||
},
|
||||
"change": {
|
||||
"question": "Soll eingebetter Fremdcode von Dritten für dich immer angezeigt werden?",
|
||||
"allow": "Na klar",
|
||||
"deny": "Lieber nicht"
|
||||
}
|
||||
}
|
||||
},
|
||||
"organizations": {
|
||||
"name": "Meine Organisationen"
|
||||
},
|
||||
@ -379,8 +412,7 @@
|
||||
"reportContent": "Melden",
|
||||
"validations": {
|
||||
"email": "muss eine gültige E-Mail Adresse sein",
|
||||
"url": "muss eine gültige URL sein",
|
||||
"verification-nonce": "muss genau 6 Buchstaben lang sein"
|
||||
"url": "muss eine gültige URL sein"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
|
||||
@ -1,4 +1,53 @@
|
||||
{
|
||||
"components": {
|
||||
"password-reset": {
|
||||
"request": {
|
||||
"title": "Reset your password",
|
||||
"form": {
|
||||
"description": "A password reset email will be sent to the given email address.",
|
||||
"submit": "Request email",
|
||||
"submitted": "An email with further instructions has been sent to <b>{email}</b>"
|
||||
}
|
||||
},
|
||||
"change-password": {
|
||||
"success": "Changing your password was successful!",
|
||||
"error": "Changing your password failed. Maybe the security code was not correct?",
|
||||
"help": "In case of problems, feel free to ask for help by sending us a mail to:"
|
||||
}
|
||||
},
|
||||
"enter-nonce": {
|
||||
"form": {
|
||||
"nonce": "Enter your code",
|
||||
"description": "Open your inbox and enter the code that we've sent to you.",
|
||||
"next": "Continue",
|
||||
"validations": {
|
||||
"length": "must be 6 characters long"
|
||||
}
|
||||
}
|
||||
},
|
||||
"registration": {
|
||||
"signup": {
|
||||
"unavailable": "Unfortunately, public registration of user accounts is not available right now on this server.",
|
||||
"title": "Join Human Connection!",
|
||||
"form": {
|
||||
"description": "To get started, enter your email address:",
|
||||
"invitation-code": "Your invitation code is: <b>{code}</b>",
|
||||
"errors": {
|
||||
"email-exists": "There is already a user account with this email address!",
|
||||
"invalid-invitation-token": "It looks like as if the invitation has been used already. Invitation links can only be used once."
|
||||
},
|
||||
"submit": "Create an account",
|
||||
"success": "A mail with a link to complete your registration has been sent to <b>{email}</b>"
|
||||
}
|
||||
},
|
||||
"create-user-account": {
|
||||
"title": "Create user account",
|
||||
"success": "Your account has been created!",
|
||||
"error": "No user account could be created!",
|
||||
"help": " Maybe the confirmation was invalid? In case of problems, feel free to ask for help by sending us a mail to:"
|
||||
}
|
||||
}
|
||||
},
|
||||
"maintenance": {
|
||||
"title": "Human Connection is under maintenance",
|
||||
"explanation": "At the moment we are doing some scheduled maintenance, please try again later.",
|
||||
@ -42,7 +91,7 @@
|
||||
"bank": "bank account",
|
||||
"germany": "Germany",
|
||||
"code-of-conduct": "Code of Conduct",
|
||||
"login": "Back to login"
|
||||
"back-to-login": "Back to login page"
|
||||
},
|
||||
"sorting": {
|
||||
"newest": "Newest",
|
||||
@ -55,51 +104,14 @@
|
||||
"email": "Your Email",
|
||||
"password": "Your Password",
|
||||
"forgotPassword": "Forgot Password?",
|
||||
"no-account": "Don't have an account?",
|
||||
"register": "Sign up",
|
||||
"moreInfo": "What is Human Connection?",
|
||||
"moreInfoURL": "https://human-connection.org/en/",
|
||||
"moreInfoHint": "to the presentation page",
|
||||
"hello": "Hello",
|
||||
"success": "You are logged in!"
|
||||
},
|
||||
"password-reset": {
|
||||
"title": "Reset your password",
|
||||
"form": {
|
||||
"description": "A password reset email will be sent to the given email address.",
|
||||
"submit": "Request email",
|
||||
"submitted": "An email with further instructions has been sent to <b>{email}</b>"
|
||||
}
|
||||
},
|
||||
"registration": {
|
||||
"signup": {
|
||||
"title": "Join Human Connection!",
|
||||
"form": {
|
||||
"description": "To get started, enter your email address:",
|
||||
"invitation-code": "Your invitation code is: <b>{code}</b>",
|
||||
"errors": {
|
||||
"email-exists": "There is already a user account with this email address!",
|
||||
"invalid-invitation-token": "It looks like as if the invitation has been used already. Invitation links can only be used once."
|
||||
},
|
||||
"submit": "Create an account",
|
||||
"success": "A mail with a link to complete your registration has been sent to <b>{email}</b>"
|
||||
}
|
||||
},
|
||||
"create-user-account": {
|
||||
"title": "Create user account",
|
||||
"success": "Your account has been created!"
|
||||
}
|
||||
},
|
||||
"verify-nonce": {
|
||||
"form": {
|
||||
"nonce": "Enter your code",
|
||||
"description": "Open your inbox and enter the code that we've sent to you.",
|
||||
"next": "Continue",
|
||||
"change-password": {
|
||||
"success": "Changing your password was successful!",
|
||||
"error": "Changing your password failed. Maybe the security code was not correct?",
|
||||
"help": "In case of problems, feel free to ask for help by sending us a mail to:"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"placeholder": "Leave your inspirational thoughts …",
|
||||
"mention": {
|
||||
@ -109,6 +121,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": {
|
||||
@ -221,6 +239,22 @@
|
||||
"success": "Account successfully deleted!",
|
||||
"pleaseConfirm": "<b class='is-danger'>Destructive action!</b> Type <b>{confirm}</b> to confirm"
|
||||
},
|
||||
"embeds": {
|
||||
"name": "Third party providers",
|
||||
"info-description": "Here is the list of third-party providers whose content can be displayed as third-party code, e.g. in the form of embedded videos.",
|
||||
"status": {
|
||||
"description": "As a default for you, embedded code of third-party providers is",
|
||||
"disabled": {
|
||||
"false": "initially not displayed",
|
||||
"true": "displayed immediately"
|
||||
},
|
||||
"change": {
|
||||
"question": "Should embedded source code from third parties always be displayed to you?",
|
||||
"allow": "Sure",
|
||||
"deny": "No thanks"
|
||||
}
|
||||
}
|
||||
},
|
||||
"organizations": {
|
||||
"name": "My Organizations"
|
||||
},
|
||||
@ -379,8 +413,7 @@
|
||||
"reportContent": "Report",
|
||||
"validations": {
|
||||
"email": "must be a valid email address",
|
||||
"url": "must be a valid URL",
|
||||
"verification-nonce": "must be 6 characters long"
|
||||
"url": "must be a valid URL"
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
|
||||
@ -1,4 +1,31 @@
|
||||
{
|
||||
"components": {
|
||||
"password-reset": {
|
||||
"request": {
|
||||
"title": "Zresetuj hasło",
|
||||
"form": {
|
||||
"description": "Na podany adres e-mail zostanie wysłany email z resetem hasła.",
|
||||
"submit": "Poproś o wiadomość e-mail",
|
||||
"submitted": "Na adres <b>{email}</b> została wysłana wiadomość z dalszymi instrukcjami."
|
||||
}
|
||||
},
|
||||
"change-password": {
|
||||
"success": "Zmiana hasła zakończyła się sukcesem!",
|
||||
"error": "Zmiana hasła nie powiodła się. Może kod bezpieczeństwa nie był poprawny?",
|
||||
"help": "W przypadku problemów, zachęcamy do zwrócenia się o pomoc, wysyłając do nas wiadomość e-mail:"
|
||||
}
|
||||
},
|
||||
"enter-nonce": {
|
||||
"form": {
|
||||
"nonce": "Wprowadź swój kod",
|
||||
"description": "Otwórz swoją skrzynkę odbiorczą i wpisz kod, który do Ciebie wysłaliśmy.",
|
||||
"next": "Kontynuuj",
|
||||
"validations": {
|
||||
"length": "musi mieć długość 6 znaków."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"filter-menu": {
|
||||
"title": "Twoja bańka filtrująca"
|
||||
},
|
||||
@ -29,26 +56,6 @@
|
||||
"moreInfoHint": "idź po więcej szczegółów",
|
||||
"hello": "Cześć"
|
||||
},
|
||||
"password-reset": {
|
||||
"title": "Zresetuj hasło",
|
||||
"form": {
|
||||
"description": "Na podany adres e-mail zostanie wysłany email z resetem hasła.",
|
||||
"submit": "Poproś o wiadomość e-mail",
|
||||
"submitted": "Na adres <b>{email}</b> została wysłana wiadomość z dalszymi instrukcjami."
|
||||
}
|
||||
},
|
||||
"verify-nonce": {
|
||||
"form": {
|
||||
"nonce": "Wprowadź swój kod",
|
||||
"description": "Otwórz swoją skrzynkę odbiorczą i wpisz kod, który do Ciebie wysłaliśmy.",
|
||||
"next": "Kontynuuj",
|
||||
"change-password": {
|
||||
"success": "Zmiana hasła zakończyła się sukcesem!",
|
||||
"error": "Zmiana hasła nie powiodła się. Może kod bezpieczeństwa nie był poprawny?",
|
||||
"help": "W przypadku problemów, zachęcamy do zwrócenia się o pomoc, wysyłając do nas wiadomość e-mail:"
|
||||
}
|
||||
}
|
||||
},
|
||||
"editor": {
|
||||
"placeholder": "Napisz coś inspirującego..."
|
||||
},
|
||||
@ -236,8 +243,7 @@
|
||||
"loading": "załadunek",
|
||||
"reportContent": "Sprawozdanie",
|
||||
"validations": {
|
||||
"email": "musi być ważny adres e-mail.",
|
||||
"verification-nonce": "musi mieć długość 6 znaków."
|
||||
"email": "musi być ważny adres e-mail."
|
||||
}
|
||||
},
|
||||
"actions": {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
import path from 'path'
|
||||
import dotenv from 'dotenv'
|
||||
|
||||
dotenv.config() // we want to synchronize @nuxt-dotenv and nuxt-env
|
||||
|
||||
const pkg = require('./package')
|
||||
export const envWhitelist = ['NODE_ENV', 'MAPBOX_TOKEN']
|
||||
export const envWhitelist = ['NODE_ENV', 'MAPBOX_TOKEN', 'PUBLIC_REGISTRATION']
|
||||
const dev = process.env.NODE_ENV !== 'production'
|
||||
|
||||
const styleguidePath = '../styleguide'
|
||||
@ -36,11 +40,10 @@ export default {
|
||||
'login',
|
||||
'logout',
|
||||
'password-reset-request',
|
||||
'password-reset-verify-nonce',
|
||||
'password-reset-enter-nonce',
|
||||
'password-reset-change-password',
|
||||
// 'registration-signup', TODO: implement to open public registration
|
||||
// 'registration-signup-by-invitation-code',
|
||||
// 'registration-verify-nonce',
|
||||
'registration-signup',
|
||||
'registration-enter-nonce',
|
||||
'registration-create-user-account',
|
||||
'pages-slug',
|
||||
'terms-and-conditions',
|
||||
|
||||
@ -92,7 +92,7 @@
|
||||
"devDependencies": {
|
||||
"@babel/core": "~7.6.3",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
||||
"@babel/preset-env": "~7.6.2",
|
||||
"@babel/preset-env": "~7.6.3",
|
||||
"@storybook/addon-a11y": "^5.2.3",
|
||||
"@storybook/addon-actions": "^5.2.3",
|
||||
"@storybook/vue": "~5.2.3",
|
||||
@ -133,4 +133,4 @@
|
||||
"vue-svg-loader": "~0.12.0",
|
||||
"vue-template-compiler": "^2.6.10"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,9 @@
|
||||
<ds-heading size="h3">{{ $t('admin.invites.title') }}</ds-heading>
|
||||
<ds-text>{{ $t('admin.invites.description') }}</ds-text>
|
||||
</ds-space>
|
||||
<signup :invitation="true" />
|
||||
<ds-card class="signup">
|
||||
<signup :invitation="true" />
|
||||
</ds-card>
|
||||
</ds-section>
|
||||
</template>
|
||||
|
||||
|
||||
@ -22,6 +22,11 @@
|
||||
</ds-space>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item :width="{ base: '100%', sm: '50%' }" centered>
|
||||
<ds-space margin="small">
|
||||
<a :href="$t('login.moreInfoURL')" :title="$t('login.moreInfoHint')" target="_blank">
|
||||
{{ $t('login.moreInfo') }}
|
||||
</a>
|
||||
</ds-space>
|
||||
<ds-space margin="small">
|
||||
<ds-text size="small">{{ $t('login.copy') }}</ds-text>
|
||||
</ds-space>
|
||||
@ -43,7 +48,7 @@
|
||||
name="password"
|
||||
type="password"
|
||||
/>
|
||||
<ds-space class="password-reset-link" margin-bottom="large">
|
||||
<ds-space margin-bottom="large">
|
||||
<nuxt-link to="/password-reset/request">{{ $t('login.forgotPassword') }}</nuxt-link>
|
||||
</ds-space>
|
||||
<ds-button
|
||||
@ -56,14 +61,9 @@
|
||||
>
|
||||
{{ $t('login.login') }}
|
||||
</ds-button>
|
||||
<ds-space margin="x-small">
|
||||
<a
|
||||
:href="$t('login.moreInfoURL')"
|
||||
:title="$t('login.moreInfoHint')"
|
||||
target="_blank"
|
||||
>
|
||||
{{ $t('login.moreInfo') }}
|
||||
</a>
|
||||
<ds-space margin-top="large" margin-bottom="x-small">
|
||||
{{ $t('login.no-account') }}
|
||||
<nuxt-link to="/registration/signup">{{ $t('login.register') }}</nuxt-link>
|
||||
</ds-space>
|
||||
</form>
|
||||
</ds-flex-item>
|
||||
|
||||
@ -1,17 +1,30 @@
|
||||
<template>
|
||||
<ds-container width="medium">
|
||||
<ds-flex>
|
||||
<ds-flex-item :width="{ base: '100%' }" centered>
|
||||
<ds-space style="text-align: center;" margin-top="small" margin-bottom="xxx-small" centered>
|
||||
<ds-container width="small">
|
||||
<ds-card>
|
||||
<ds-flex gutter="small">
|
||||
<ds-flex-item :width="{ base: '100%', sm: '40%' }">
|
||||
<client-only>
|
||||
<locale-switch offset="5" />
|
||||
</client-only>
|
||||
<ds-space margin-top="small" margin-bottom="xxx-small" centered>
|
||||
<img alt="Human Connection" src="/icon.png" />
|
||||
</ds-space>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item :width="{ base: '100%', sm: '60%' }" centered>
|
||||
<nuxt-child />
|
||||
</ds-space>
|
||||
</ds-flex-item>
|
||||
</ds-flex>
|
||||
</ds-flex-item>
|
||||
</ds-flex>
|
||||
</ds-card>
|
||||
</ds-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import LocaleSwitch from '~/components/LocaleSwitch/LocaleSwitch'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
LocaleSwitch,
|
||||
},
|
||||
layout: 'no-header',
|
||||
asyncData({ store, redirect }) {
|
||||
if (store.getters['auth/isLoggedIn']) {
|
||||
@ -20,3 +33,10 @@ export default {
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
img {
|
||||
padding-left: 50px;
|
||||
padding-right: 50px;
|
||||
max-width: 200px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -3,7 +3,11 @@
|
||||
:email="email"
|
||||
:nonce="nonce"
|
||||
@passwordResetResponse="handlePasswordResetResponse"
|
||||
/>
|
||||
>
|
||||
<ds-space centered>
|
||||
<nuxt-link to="/login">{{ $t('site.back-to-login') }}</nuxt-link>
|
||||
</ds-space>
|
||||
</change-password>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
26
webapp/pages/password-reset/enter-nonce.vue
Normal file
26
webapp/pages/password-reset/enter-nonce.vue
Normal file
@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<enter-nonce :email="email" @nonceEntered="nonceEntered">
|
||||
<ds-space margin-bottom="xxx-small" margin-top="large" centered>
|
||||
<nuxt-link to="/login">{{ $t('site.back-to-login') }}</nuxt-link>
|
||||
</ds-space>
|
||||
</enter-nonce>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import EnterNonce from '~/components/EnterNonce/EnterNonce.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
EnterNonce,
|
||||
},
|
||||
data() {
|
||||
const { email = '' } = this.$route.query
|
||||
return { email }
|
||||
},
|
||||
methods: {
|
||||
nonceEntered({ email, nonce }) {
|
||||
this.$router.push({ path: 'change-password', query: { email, nonce } })
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -1,5 +1,9 @@
|
||||
<template>
|
||||
<request @handleSubmitted="handlePasswordResetRequested" />
|
||||
<request @handleSubmitted="handlePasswordResetRequested">
|
||||
<ds-space margin-bottom="xxx-small" margin-top="large" centered>
|
||||
<nuxt-link to="/login">{{ $t('site.back-to-login') }}</nuxt-link>
|
||||
</ds-space>
|
||||
</request>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -11,7 +15,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
handlePasswordResetRequested({ email }) {
|
||||
this.$router.push({ path: 'verify-nonce', query: { email } })
|
||||
this.$router.push({ path: 'enter-nonce', query: { email } })
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -1,22 +0,0 @@
|
||||
<template>
|
||||
<verify-nonce :email="email" @verification="handleVerification" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VerifyNonce from '~/components/PasswordReset/VerifyNonce'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
VerifyNonce,
|
||||
},
|
||||
data() {
|
||||
const { email = '' } = this.$route.query
|
||||
return { email }
|
||||
},
|
||||
methods: {
|
||||
handleVerification({ email, nonce }) {
|
||||
this.$router.push({ path: 'change-password', query: { email, nonce } })
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -1,9 +1,29 @@
|
||||
<template>
|
||||
<nuxt-child />
|
||||
<ds-container width="medium">
|
||||
<ds-card>
|
||||
<ds-flex gutter="small">
|
||||
<ds-flex-item :width="{ base: '100%', sm: '50%' }">
|
||||
<client-only>
|
||||
<locale-switch offset="5" />
|
||||
</client-only>
|
||||
<ds-space margin-top="small" margin-bottom="xxx-small">
|
||||
<img class="signup-image" alt="Human Connection" src="/img/sign-up/nicetomeetyou.svg" />
|
||||
</ds-space>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item :width="{ base: '100%', sm: '50%' }" centered>
|
||||
<nuxt-child />
|
||||
</ds-flex-item>
|
||||
</ds-flex>
|
||||
</ds-card>
|
||||
</ds-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import LocaleSwitch from '~/components/LocaleSwitch/LocaleSwitch'
|
||||
export default {
|
||||
components: {
|
||||
LocaleSwitch,
|
||||
},
|
||||
layout: 'no-header',
|
||||
asyncData({ store, redirect }) {
|
||||
if (store.getters['auth/isLoggedIn']) {
|
||||
|
||||
25
webapp/pages/registration/enter-nonce.vue
Normal file
25
webapp/pages/registration/enter-nonce.vue
Normal file
@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<enter-nonce :email="email" @nonceEntered="nonceEntered">
|
||||
<ds-space margin-bottom="xxx-small" margin-top="large" centered>
|
||||
<nuxt-link to="/login">{{ $t('site.back-to-login') }}</nuxt-link>
|
||||
</ds-space>
|
||||
</enter-nonce>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import EnterNonce from '~/components/EnterNonce/EnterNonce.vue'
|
||||
export default {
|
||||
components: {
|
||||
EnterNonce,
|
||||
},
|
||||
data() {
|
||||
const { email = '' } = this.$route.query
|
||||
return { email }
|
||||
},
|
||||
methods: {
|
||||
nonceEntered({ email, nonce }) {
|
||||
this.$router.push({ path: 'create-user-account', query: { email, nonce } })
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
34
webapp/pages/registration/signup.vue
Normal file
34
webapp/pages/registration/signup.vue
Normal file
@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<signup v-if="publicRegistration" :invitation="false" @submit="handleSubmitted">
|
||||
<ds-space centered margin-top="large">
|
||||
<nuxt-link to="/login">{{ $t('site.back-to-login') }}</nuxt-link>
|
||||
</ds-space>
|
||||
</signup>
|
||||
<ds-space v-else centered>
|
||||
<hc-empty icon="events" :message="$t('components.registration.signup.unavailable')" />
|
||||
<nuxt-link to="/login">{{ $t('site.back-to-login') }}</nuxt-link>
|
||||
</ds-space>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Signup from '~/components/Registration/Signup'
|
||||
import HcEmpty from '~/components/Empty.vue'
|
||||
|
||||
export default {
|
||||
layout: 'no-header',
|
||||
components: {
|
||||
HcEmpty,
|
||||
Signup,
|
||||
},
|
||||
asyncData({ app }) {
|
||||
return {
|
||||
publicRegistration: app.$env.PUBLIC_REGISTRATION === 'true',
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSubmitted({ email }) {
|
||||
this.$router.push({ path: 'enter-nonce', query: { email } })
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -39,6 +39,10 @@ export default {
|
||||
name: this.$t('settings.blocked-users.name'),
|
||||
path: `/settings/blocked-users`,
|
||||
},
|
||||
{
|
||||
name: this.$t('settings.embeds.name'),
|
||||
path: `/settings/embeds`,
|
||||
},
|
||||
{
|
||||
name: this.$t('settings.deleteUserAccount.name'),
|
||||
path: `/settings/delete-account`,
|
||||
|
||||
87
webapp/pages/settings/embeds.vue
Normal file
87
webapp/pages/settings/embeds.vue
Normal file
@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<ds-card :header="$t('settings.embeds.name')">
|
||||
<ds-section>
|
||||
<ds-text>
|
||||
{{ $t('settings.embeds.status.description') }}
|
||||
<ds-text bold>
|
||||
{{ $t(`settings.embeds.status.disabled.${disabled}`) }}
|
||||
</ds-text>
|
||||
.
|
||||
</ds-text>
|
||||
<ds-text>
|
||||
{{ $t('settings.embeds.status.change.question') }}
|
||||
</ds-text>
|
||||
<ds-button @click="submit" :primary="!disabled" :disabled="!disabled">
|
||||
{{ $t('settings.embeds.status.change.deny') }}
|
||||
</ds-button>
|
||||
<ds-button @click="submit" :primary="disabled" :disabled="disabled">
|
||||
{{ $t('settings.embeds.status.change.allow') }}
|
||||
</ds-button>
|
||||
|
||||
<p>{{ $t('settings.embeds.info-description') }}</p>
|
||||
<ul>
|
||||
<li v-for="provider in providers" :key="provider.provider_name">
|
||||
{{ provider.provider_name }},
|
||||
<small>{{ provider.provider_url }}</small>
|
||||
</li>
|
||||
</ul>
|
||||
</ds-section>
|
||||
</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('settings.embeds.name'),
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
currentUser: 'auth/user',
|
||||
}),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
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',
|
||||
}),
|
||||
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.disabled = !this.disabled
|
||||
} catch (err) {
|
||||
this.$toast.error(err.message)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@ -86,6 +86,7 @@ export const actions = {
|
||||
locationName
|
||||
contributionsCount
|
||||
commentedCount
|
||||
allowEmbedIframes
|
||||
termsAndConditionsAgreedVersion
|
||||
socialMedia {
|
||||
id
|
||||
|
||||
@ -438,10 +438,10 @@
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.0.0"
|
||||
|
||||
"@babel/plugin-transform-block-scoping@^7.6.2":
|
||||
version "7.6.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.6.2.tgz#96c33ab97a9ae500cc6f5b19e04a7e6553360a79"
|
||||
integrity sha512-zZT8ivau9LOQQaOGC7bQLQOT4XPkPXgN2ERfUgk1X8ql+mVkLc4E8eKk+FO3o0154kxzqenWCorfmEXpEZcrSQ==
|
||||
"@babel/plugin-transform-block-scoping@^7.6.3":
|
||||
version "7.6.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.6.3.tgz#6e854e51fbbaa84351b15d4ddafe342f3a5d542a"
|
||||
integrity sha512-7hvrg75dubcO3ZI2rjYTzUrEuh1E9IyDEhhB6qfcooxhDA33xx2MasuLVgdxzcP6R/lipAC6n9ub9maNW6RKdw==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.0.0"
|
||||
lodash "^4.17.13"
|
||||
@ -563,10 +563,10 @@
|
||||
"@babel/helper-module-transforms" "^7.1.0"
|
||||
"@babel/helper-plugin-utils" "^7.0.0"
|
||||
|
||||
"@babel/plugin-transform-named-capturing-groups-regex@^7.6.2":
|
||||
version "7.6.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.6.2.tgz#c1ca0bb84b94f385ca302c3932e870b0fb0e522b"
|
||||
integrity sha512-xBdB+XOs+lgbZc2/4F5BVDVcDNS4tcSKQc96KmlqLEAwz6tpYPEvPdmDfvVG0Ssn8lAhronaRs6Z6KSexIpK5g==
|
||||
"@babel/plugin-transform-named-capturing-groups-regex@^7.6.3":
|
||||
version "7.6.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.6.3.tgz#aaa6e409dd4fb2e50b6e2a91f7e3a3149dbce0cf"
|
||||
integrity sha512-jTkk7/uE6H2s5w6VlMHeWuH+Pcy2lmdwFoeWCVnvIrDUnB5gQqTVI8WfmEAhF2CDEarGrknZcmSFg1+bkfCoSw==
|
||||
dependencies:
|
||||
regexpu-core "^4.6.0"
|
||||
|
||||
@ -679,10 +679,10 @@
|
||||
"@babel/helper-regex" "^7.4.4"
|
||||
regexpu-core "^4.6.0"
|
||||
|
||||
"@babel/preset-env@^7.4.5", "@babel/preset-env@^7.6.2", "@babel/preset-env@~7.6.2":
|
||||
version "7.6.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.6.2.tgz#abbb3ed785c7fe4220d4c82a53621d71fc0c75d3"
|
||||
integrity sha512-Ru7+mfzy9M1/YTEtlDS8CD45jd22ngb9tXnn64DvQK3ooyqSw9K4K9DUWmYknTTVk4TqygL9dqCrZgm1HMea/Q==
|
||||
"@babel/preset-env@^7.4.5", "@babel/preset-env@^7.6.2", "@babel/preset-env@~7.6.3":
|
||||
version "7.6.3"
|
||||
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.6.3.tgz#9e1bf05a2e2d687036d24c40e4639dc46cef2271"
|
||||
integrity sha512-CWQkn7EVnwzlOdR5NOm2+pfgSNEZmvGjOhlCHBDq0J8/EStr+G+FvPEiz9B56dR6MoiUFjXhfE4hjLoAKKJtIQ==
|
||||
dependencies:
|
||||
"@babel/helper-module-imports" "^7.0.0"
|
||||
"@babel/helper-plugin-utils" "^7.0.0"
|
||||
@ -700,7 +700,7 @@
|
||||
"@babel/plugin-transform-arrow-functions" "^7.2.0"
|
||||
"@babel/plugin-transform-async-to-generator" "^7.5.0"
|
||||
"@babel/plugin-transform-block-scoped-functions" "^7.2.0"
|
||||
"@babel/plugin-transform-block-scoping" "^7.6.2"
|
||||
"@babel/plugin-transform-block-scoping" "^7.6.3"
|
||||
"@babel/plugin-transform-classes" "^7.5.5"
|
||||
"@babel/plugin-transform-computed-properties" "^7.2.0"
|
||||
"@babel/plugin-transform-destructuring" "^7.6.0"
|
||||
@ -715,7 +715,7 @@
|
||||
"@babel/plugin-transform-modules-commonjs" "^7.6.0"
|
||||
"@babel/plugin-transform-modules-systemjs" "^7.5.0"
|
||||
"@babel/plugin-transform-modules-umd" "^7.2.0"
|
||||
"@babel/plugin-transform-named-capturing-groups-regex" "^7.6.2"
|
||||
"@babel/plugin-transform-named-capturing-groups-regex" "^7.6.3"
|
||||
"@babel/plugin-transform-new-target" "^7.4.4"
|
||||
"@babel/plugin-transform-object-super" "^7.5.5"
|
||||
"@babel/plugin-transform-parameters" "^7.4.4"
|
||||
@ -728,7 +728,7 @@
|
||||
"@babel/plugin-transform-template-literals" "^7.4.4"
|
||||
"@babel/plugin-transform-typeof-symbol" "^7.2.0"
|
||||
"@babel/plugin-transform-unicode-regex" "^7.6.2"
|
||||
"@babel/types" "^7.6.0"
|
||||
"@babel/types" "^7.6.3"
|
||||
browserslist "^4.6.0"
|
||||
core-js-compat "^3.1.1"
|
||||
invariant "^2.2.2"
|
||||
|
||||
@ -1877,10 +1877,10 @@ cucumber@^4.2.1:
|
||||
util-arity "^1.0.2"
|
||||
verror "^1.9.0"
|
||||
|
||||
cypress-cucumber-preprocessor@^1.16.1:
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/cypress-cucumber-preprocessor/-/cypress-cucumber-preprocessor-1.16.1.tgz#2ac7e0e53396334c052aeed8b5e61e08616f73a2"
|
||||
integrity sha512-m8Z5t9hSc10kv47qK0fV/JlCboCwQVxgTa+WhnCjOPB7YBnX/en4f0O8l27yaZbZyHan7JBoJvpfzINlaOKafg==
|
||||
cypress-cucumber-preprocessor@^1.16.2:
|
||||
version "1.16.2"
|
||||
resolved "https://registry.yarnpkg.com/cypress-cucumber-preprocessor/-/cypress-cucumber-preprocessor-1.16.2.tgz#79e0ce7e7afa781f752711f7284a3bf48aa99ab8"
|
||||
integrity sha512-jDJuQnnzrOrO+4PRt+VKFkHxHO7DplJACXOMUHLLWcL7vjlRUkIG4+QWnOkn/Py3yOhv9Rmuug8Iil5+FV8wEw==
|
||||
dependencies:
|
||||
"@cypress/browserify-preprocessor" "^2.1.1"
|
||||
chai "^4.1.2"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user