Skip to content
Merged

Gdpr #1646

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"blueimp-load-image": "5.14.0",
"bulma": "0.9.2",
"bulma-checkradio": "1.1.1",
"bulma-switch": "2.0.0",
"bulma-toast": "2.2.0",
"core-js": "3.8.3",
"d3": "6.5.0",
Expand Down
3 changes: 3 additions & 0 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@
<div class="page-content is-block-print">
<router-view class="router-view" />
</div>
<gdpr-banner></gdpr-banner>
</div>
</template>

<script>
import AlertWindow from './components/alert-window/AlertWindow';
import GdprBanner from './components/gdpr/GdprBanner.vue';
import HelperWindow from './components/helper/HelperWindow';
import ImageViewer from './components/image-viewer/ImageViewer';
import Navigation from './views/Navigation';
Expand All @@ -33,6 +35,7 @@ export default {
HelperWindow,
ImageViewer,
AlertWindow,
GdprBanner,
},

data() {
Expand Down
2 changes: 2 additions & 0 deletions src/assets/sass/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,7 @@ $button-padding-horizontal: 0.75em;
@import '~bulma-checkradio';
// bulma checkradio isn't customizable with variables. Overrides its classes
@import './checkradio-customization.scss';
//
@import '~bulma-switch';

@import './helpers.scss';
104 changes: 104 additions & 0 deletions src/components/gdpr/GdprBanner.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<template>
<div>
<div class="gdpr-content" :class="{ active: active }">
<h4 class="title" v-translate>Control the use of your personal data</h4>
<div class="message">
<span v-translate class="has-text-justified">
Our website uses cookies provided by ourselves and third parties. Some cookies are necessary for the website,
while you can adjust others at any time, in particular those that allow us to understand the performance of
our website and to offer you social media features. You can accept or reject all, or
</span>
<a @click="showGdprModal()" v-translate>configure your choices</a>.
</div>
<div class="buttons mt-2 is-flex is-justify-content-flex-end">
<button @click="acceptGdpr(false)" class="button is-danger" v-translate>Deny all</button>
<button @click="acceptGdpr(true)" class="button is-primary" v-translate>Allow all</button>
</div>
</div>
<gdpr-modal ref="GdprModal"></gdpr-modal>
</div>
</template>

<script>
import GdprModal from './GdprModal.vue';

export default {
components: { GdprModal },

data() {
return {
userHasInteracted: false,
};
},

computed: {
active: function () {
return !this.$gdpr.get() && this.userHasInteracted;
},
},

beforeMount() {
window.addEventListener('scroll', this.firstUserInteraction);
window.addEventListener('keydown', this.firstUserInteraction);
window.addEventListener('resize', this.firstUserInteraction);
window.addEventListener('click', this.firstUserInteraction);
},

mounted() {
this.$root.$on('showGdpr', () => this.showGdprModal());
},

methods: {
showGdprModal() {
this.$refs.GdprModal.show(this.gdprValue);
},

acceptGdpr(accept) {
this.$gdpr.setAll(accept);
},

firstUserInteraction() {
window.removeEventListener('scroll', this.firstUserInteraction);
window.removeEventListener('keydown', this.firstUserInteraction);
window.removeEventListener('resize', this.firstUserInteraction);
window.removeEventListener('click', this.firstUserInteraction);

this.userHasInteracted = true;
},
},
};
</script>

<style lang="scss" scoped>
@import '@/assets/sass/variables.scss';

.gdpr-content {
z-index: 30;
position: fixed;
top: 0;
padding: 10px;

margin: 0 auto;
flex-direction: column;
display: flex;
background-color: white;
box-shadow: none;
transform: translateY(-100%);
transition: transform 0.25s ease-in-out;

&.active {
box-shadow: rgba(0, 0, 0, 0.3) 1px 1px 6px 0;
transform: translateY(0);
}
}

@media screen and (min-width: $desktop) {
.gdpr-content {
width: 50%;
left: calc(25% - 10px);

border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
}
}
</style>
87 changes: 87 additions & 0 deletions src/components/gdpr/GdprModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<template>
<modal-window ref="modalWindow">
<div slot="header" class="has-text-centered">
<fa-icon icon="user-shield"></fa-icon>
<span v-translate>Configure cookies</span>
</div>

<div class="columns">
<div class="column">
<h2 class="is-size-3" v-translate>Necessary cookies</h2>
<p v-translate>
Necessary cookies are essential for the proper functioning of our website and cannot be disabled. They are
sent to your computer or device when you ask for a specific action or service, e.g. when you log in, fill out
a form or define your cookie preferences. If you configure your computer to block these cookies or to warn you
of their existence, our website will not function fully.
</p>
</div>
<div class="column">
<div class="is-flex is-justify-content-space-between is-align-items-baseline">
<h2 class="is-size-3" v-translate>Statistical cookies</h2>
<div class="field">
<input
type="checkbox"
id="statistics"
name="statistics"
class="switch is-rounded"
v-model="gdpr.statistics"
/>
<label for="statistics"></label>
</div>
</div>
<p v-translate>
Thanks to the statistical cookies provided by ourselves and other companies, we can evaluate the visit on our
website and know the sources of traffic. Data we obtain help us understand what visitors like and improve our
website. If you reject them, we cannot improve your experience.
</p>
<div class="is-flex is-justify-content-space-between is-align-items-baseline">
<h2 class="is-size-3" v-translate>Social cookies</h2>
<div class="field">
<input type="checkbox" id="social" name="social" class="switch is-rounded" v-model="gdpr.social" />
<label for="social"></label>
</div>
</div>
<p v-translate>
These cookies allow you to interact from camptocamp.org website with social media modules and share content of
the website with other people or let them know of your consultation or opinion on it, e.g. when you click on
the "Share" module. By disabling these cookies, you won't be able to share content from camptocamp.org on
social networks.
</p>
</div>
</div>

<div slot="footer" class="buttons is-flex is-justify-content-flex-end">
<button class="button" @click="hide" v-translate>Cancel</button>
<button class="button is-primary" @click="submit" v-translate>Submit</button>
</div>
</modal-window>
</template>

<script>
export default {
data() {
return {
gdpr: {
statistics: false,
social: false,
},
};
},

methods: {
show() {
this.gdpr = JSON.parse(JSON.stringify(this.$gdpr.get())) || { statistics: false, social: false };
this.$refs.modalWindow.show();
},

hide() {
this.$refs.modalWindow.hide();
},

submit() {
this.$gdpr.set(this.gdpr);
this.hide();
},
},
};
</script>
5 changes: 4 additions & 1 deletion src/components/generics/modals/ModalWindow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
</header>
<button v-else class="delete is-pulled-right" aria-label="close" @click="hide" />
<slot> Modal content </slot>
<footer v-if="$slots.footer" class="is-3">
<slot name="footer"></slot>
</footer>
</div>
</div>
</template>
Expand Down Expand Up @@ -42,7 +45,7 @@ export default {
<style scoped>
.modal-content {
background: white;
border-radius: 6px;
border-radius: 4px;
padding: 1.5rem;
}

Expand Down
2 changes: 2 additions & 0 deletions src/js/vue-plugins/font-awesome-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ import { faUpload } from '@fortawesome/free-solid-svg-icons/faUpload';
import { faUser } from '@fortawesome/free-solid-svg-icons/faUser';
import { faUserCheck } from '@fortawesome/free-solid-svg-icons/faUserCheck';
import { faUserLock } from '@fortawesome/free-solid-svg-icons/faUserLock';
import { faUserShield } from '@fortawesome/free-solid-svg-icons/faUserShield';
import { faUsers } from '@fortawesome/free-solid-svg-icons/faUsers';
import { faWrench } from '@fortawesome/free-solid-svg-icons/faWrench';
import { FontAwesomeIcon, FontAwesomeLayers } from '@fortawesome/vue-fontawesome';
Expand Down Expand Up @@ -302,6 +303,7 @@ export default function install(Vue) {
faUserCheck,
faUserLock,
faUsers,
faUserShield,
faWrench,

// regular icons
Expand Down
53 changes: 53 additions & 0 deletions src/js/vue-plugins/gdpr.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
export default function install(Vue) {
const gdprVm = new Vue({
name: 'Gdpr',

data() {
return { gdprValue: undefined };
},

created() {
try {
let storedValue = this.$localStorage.get('choice');
// if choice is over a year, consent must be asked again
if (Date.now() - (storedValue?.date ?? 0) > 1000 * 60 * 60 * 24 * 365) {
storedValue = null;
}
this.gdprValue = storedValue;
} catch (err) {
this.gdprValue = undefined;
}

if (this.gdprValue?.statistics) {
this.$ga.enable();
}
},

methods: {
get() {
return this.gdprValue;
},

set(newValue) {
if (newValue) {
this.gdprValue = { ...newValue, date: Date.now() };
this.$localStorage.set('choice', this.gdprValue);
} else {
this.gdprValue = undefined;
}

if (newValue?.statistics) {
this.$ga.enable();
} else {
this.$ga.disable();
}
},

setAll(accept) {
this.set(accept ? { statistics: true, social: true } : { statistics: false, social: false });
},
},
});

Vue.prototype.$gdpr = gdprVm;
}
Loading