Skip to content

Commit

Permalink
Force the user to scroll text to accept the TOS
Browse files Browse the repository at this point in the history
Signed-off-by: greta <[email protected]>
  • Loading branch information
GretaD committed Jul 19, 2024
1 parent 45eb26e commit 7c79011
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 45 deletions.
6 changes: 4 additions & 2 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<template>
<Fragment>
<NcSettingsSection :title="t('terms_of_service', 'Terms of service')"
<NcSettingsSection :name="t('terms_of_service', 'Terms of service')"
:description="t('terms_of_service', 'Require users to accept the terms of service before accessing the service.')">
<NcCheckboxRadioSwitch type="switch"
:checked.sync="showForLoggedInUser">
Expand All @@ -21,11 +21,13 @@
<NcSelect v-model="country"
:options="countryOptions"
:placeholder="t('terms_of_service', 'Select a region')"
:aria-label-combobox="t('terms_of_service', 'Select a region')"
label="label"
track-by="value" />
<NcSelect v-model="language"
:options="languageOptions"
:placeholder="t('terms_of_service', 'Select a language')"
:aria-label-combobox="t('terms_of_service', 'Select a language')"
label="label"
track-by="value" />
</span>
Expand All @@ -45,7 +47,7 @@
</NcSettingsSection>

<NcSettingsSection v-if="hasTerms"
:title="t('terms_of_service', 'Existing terms of service')">
:name="t('terms_of_service', 'Existing terms of service')">
<NcButton :disabled="resetButtonDisabled"
type="error"
@click="onResetSignatories">
Expand Down
16 changes: 14 additions & 2 deletions src/Registration.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<NcModal v-if="showModal"
:can-close="hasSigned"
@close="handleCloseModal">
<ModalContent @click="acceptTerms">
<ModalContent :is-scroll-complete="hasScrolledToBottom" @click="acceptTerms">
<template #header>
<h3>{{ t('terms_of_service', 'Terms of service') }}</h3>
<select v-if="terms.length > 1" v-model="selectedLanguage">
Expand All @@ -35,7 +35,10 @@
</template>

<!-- eslint-disable-next-line vue/no-v-html -->
<div class="text-content" v-html="termsBody" />
<div ref="termsContent"
class="text-content"
@scroll="checkScroll"
v-html="termsBody" />

Check warning on line 41 in src/Registration.vue

View workflow job for this annotation

GitHub Actions / NPM lint

'v-html' directive can lead to XSS attack
</ModalContent>
</NcModal>
</div>
Expand Down Expand Up @@ -68,6 +71,7 @@ export default {
termsId: 0,
termsBody: '',
publicContent: null,
hasScrolledToBottom: false,
}
},

Expand Down Expand Up @@ -128,6 +132,14 @@ export default {
return
}
this.showModal = true
this.$nextTick(() => {
this.checkScroll()
})
},
checkScroll() {
const termsContent = this.$refs.termsContent
const isScrollable = termsContent.scrollHeight > termsContent.clientHeight
this.hasScrolledToBottom = !isScrollable || (termsContent.scrollHeight - termsContent.scrollTop <= termsContent.clientHeight + 1)
},

acceptTerms() {
Expand Down
83 changes: 48 additions & 35 deletions src/UserApp.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<NcModal v-if="showModal"
:can-close="hasSigned"
@close="handleCloseModal">
<ModalContent @click="acceptTerms">
<ModalContent :is-scroll-complete="hasScrolledToBottom" @click="acceptTerms">
<template #header>
<h3>{{ t('terms_of_service', 'Terms of service') }}</h3>
<select v-if="terms.length > 1" v-model="selectedLanguage">
Expand All @@ -19,7 +19,10 @@
</template>

<!-- eslint-disable-next-line vue/no-v-html -->
<div class="text-content" v-html="termsBody" />
<div ref="termsContent"
class="text-content"
@scroll="checkScroll"
v-html="termsBody" />

Check warning on line 25 in src/UserApp.vue

View workflow job for this annotation

GitHub Actions / NPM lint

'v-html' directive can lead to XSS attack
</ModalContent>
</NcModal>
</div>
Expand Down Expand Up @@ -49,6 +52,7 @@ export default {
termsId: 0,
termsBody: '',
publicContent: null,
hasScrolledToBottom: false,
}
},

Expand All @@ -63,38 +67,38 @@ export default {
},

methods: {
loadTerms() {
axios
.get(generateUrl('/apps/terms_of_service/terms'))
.then(response => {
this.hasSigned = response.data.hasSigned
this.terms = response.data.terms

const language = OC.getLanguage().split('-')[0]

if (!this.terms.length || this.hasSigned) {
return
}

// make it Vue
this.publicContent = document.getElementById('files-public-content')
if (this.publicContent !== null) {
this.publicContent.style.visibility = 'hidden'
}

this.selectTerms(0)
if (this.terms.length > 1) {
Object.keys(this.terms).forEach((index) => {
if (language === this.terms[index].languageCode) {
this.selectedLanguage = index
}

this.languages.push(response.data.languages[this.terms[index].languageCode])
})
}

this.showTerms()
})
async loadTerms() {
try {
const response = await axios.get(generateUrl('/apps/terms_of_service/terms'))
this.hasSigned = response.data.hasSigned
this.terms = response.data.terms

const language = OC.getLanguage().split('-')[0]

if (!this.terms.length || this.hasSigned) {
return
}

this.publicContent = document.getElementById('files-public-content')
if (this.publicContent !== null) {
this.publicContent.style.visibility = 'hidden'
}

this.selectTerms(0)
if (this.terms.length > 1) {
Object.keys(this.terms).forEach((index) => {
if (language === this.terms[index].languageCode) {
this.selectedLanguage = index
}

this.languages.push(response.data.languages[this.terms[index].languageCode])
})
}

this.showTerms()
} catch (error) {
console.error('Error loading terms:', error)
}
},

selectTerms(index) {
Expand All @@ -104,6 +108,9 @@ export default {

showTerms() {
this.showModal = true
this.$nextTick(() => {
this.checkScroll()
})
},

acceptTerms() {
Expand All @@ -126,12 +133,18 @@ export default {
handleCloseModal() {
this.showModal = false
},

checkScroll() {
const termsContent = this.$refs.termsContent
const isScrollable = termsContent.scrollHeight > termsContent.clientHeight

this.hasScrolledToBottom = !isScrollable || (termsContent.scrollHeight - termsContent.scrollTop <= termsContent.clientHeight + 1)
},
},
}
</script>

<style lang="scss" scoped>

:deep .modal-container {
display: flex;
height: 100%;
Expand Down
32 changes: 26 additions & 6 deletions src/components/ModalContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
- SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->

<template>
<div id="terms_of_service_content"
class="modal-content"
Expand All @@ -13,7 +12,9 @@
</div>

<!-- Scrollable terms of service -->
<slot />
<div ref="termsContent" class="terms-content">
<slot />
</div>

<!-- Sticky button -->
<NcButton ref="acceptButton"
Expand All @@ -22,6 +23,7 @@
:wide="true"
autofocus
:title="t('terms_of_service', 'I acknowledge that I have read and agree to the above terms of service')"
:disabled="!isScrollComplete"
@click.prevent.stop="handleClick"
@keydown.enter="handleClick">
{{ t('terms_of_service', 'I acknowledge that I have read and agree to the above terms of service') }}
Expand All @@ -39,6 +41,13 @@ export default {
NcButton,
},

props: {
isScrollComplete: {
type: Boolean,
default: false,
},
},

mounted() {
this.$nextTick(() => {
this.$refs.acceptButton.$el.focus()
Expand All @@ -47,12 +56,13 @@ export default {

methods: {
handleClick() {
this.$emit('click')
if (this.isScrollComplete) {
this.$emit('click')
}
},
},
}
</script>

<style lang="scss" scoped>
/* Little hack to strengthen the css selector so links with dark mode on the registration page are readable */
#terms_of_service_content.modal-content,
Expand All @@ -76,10 +86,15 @@ export default {
margin: 8px 0 12px 0;
}

.terms-content {
height: 100%;
overflow-y: auto;
flex: 1;
}

select {
float: right;
padding: 0 12px;

/**
* Need to overwrite the rules of guest.css
*/
Expand All @@ -90,7 +105,6 @@ export default {
border-color: var(--color-primary-element);
}
}

/**
* Basic Markdown support
*/
Expand Down Expand Up @@ -123,5 +137,11 @@ export default {
}
}
}
</style>

<style lang="scss" scoped>
:deep .modal-container {
display: flex;
height: 100%;
}
</style>

0 comments on commit 7c79011

Please sign in to comment.