Client - init privacy policy and instance description

This commit is contained in:
Sam 2023-02-26 18:25:25 +01:00
parent 8a3f9a5d59
commit 3834e71c95
33 changed files with 359 additions and 26 deletions

View File

@ -30,6 +30,7 @@
"linkifyjs": "^4.0.2", "linkifyjs": "^4.0.2",
"register-service-worker": "^1.7.1", "register-service-worker": "^1.7.1",
"sanitize-html": "^2.10.0", "sanitize-html": "^2.10.0",
"snarkdown": "^2.0.0",
"vue": "^3.2.45", "vue": "^3.2.45",
"vue-chart-3": "3.1.1", "vue-chart-3": "3.1.1",
"vue-fullscreen": "^3.1.1", "vue-fullscreen": "^3.1.1",

View File

@ -46,16 +46,24 @@
{{ weather_provider.name }} {{ weather_provider.name }}
</a> </a>
</div> </div>
<template v-if="appConfig.about">
<p class="about-instance">{{ $t('about.ABOUT_THIS_INSTANCE') }}</p>
<div
v-html="snarkdown(linkifyAndClean(appConfig.about))"
/>
</template>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import snarkdown from 'snarkdown'
import { ComputedRef, computed, capitalize } from 'vue' import { ComputedRef, computed, capitalize } from 'vue'
import { ROOT_STORE } from '@/store/constants' import { ROOT_STORE } from '@/store/constants'
import { TAppConfig } from '@/types/application' import { TAppConfig } from '@/types/application'
import { useStore } from '@/use/useStore' import { useStore } from '@/use/useStore'
import { linkifyAndClean } from '@/utils/inputs'
const store = useStore() const store = useStore()
const appConfig: ComputedRef<TAppConfig> = computed( const appConfig: ComputedRef<TAppConfig> = computed(
@ -84,11 +92,17 @@
.about-text { .about-text {
margin-top: 200px; margin-top: 200px;
margin-right: 100px;
@media screen and (max-width: $small-limit) { @media screen and (max-width: $small-limit) {
margin-top: 0; margin-top: 0;
margin-right: 0;
} }
.fa-padding { .fa-padding {
padding-right: $default-padding; padding-right: $default-padding;
} }
.about-instance {
font-weight: bold;
margin-top: $default-margin*3;
}
} }
</style> </style>

View File

@ -73,6 +73,42 @@
:disabled="!edition" :disabled="!edition"
/> />
</label> </label>
<label class="about-label" for="about">
{{ $t('admin.ABOUT.TEXT') }}:
</label>
<span class="textarea-description">
{{ $t('admin.ABOUT.DESCRIPTION') }}
</span>
<textarea
v-if="edition"
id="about"
name="about"
rows="10"
v-model="appData.about"
/>
<div
v-else
v-html="snarkdown(linkifyAndClean(appData.about ? appData.about : $t('admin.NO_TEXT_ENTERED')))"
class="textarea-content"
/>
<label class="privacy-policy-label" for="privacy_policy">
{{ capitalize($t('privacy_policy.TITLE')) }}:
</label>
<span class="textarea-description">
{{ $t('admin.PRIVACY_POLICY_DESCRIPTION') }}
</span>
<textarea
v-if="edition"
id="privacy_policy"
name="privacy_policy"
rows="20"
v-model="appData.privacy_policy"
/>
<div
v-else
v-html="snarkdown(linkifyAndClean(appData.privacy_policy ? appData.privacy_policy : $t('admin.NO_TEXT_ENTERED')))"
class="textarea-content"
/>
<ErrorMessage :message="errorMessages" v-if="errorMessages" /> <ErrorMessage :message="errorMessages" v-if="errorMessages" />
<div class="form-buttons" v-if="edition"> <div class="form-buttons" v-if="edition">
<button class="confirm" type="submit"> <button class="confirm" type="submit">
@ -100,8 +136,10 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import snarkdown from 'snarkdown'
import { import {
ComputedRef, ComputedRef,
capitalize,
computed, computed,
reactive, reactive,
withDefaults, withDefaults,
@ -114,6 +152,7 @@
import { TAppConfig, TAppConfigForm } from '@/types/application' import { TAppConfig, TAppConfigForm } from '@/types/application'
import { useStore } from '@/use/useStore' import { useStore } from '@/use/useStore'
import { getFileSizeInMB } from '@/utils/files' import { getFileSizeInMB } from '@/utils/files'
import { linkifyAndClean } from '@/utils/inputs'
interface Props { interface Props {
appConfig: TAppConfig appConfig: TAppConfig
@ -133,6 +172,8 @@
max_single_file_size: 0, max_single_file_size: 0,
max_zip_file_size: 0, max_zip_file_size: 0,
gpx_limit_import: 0, gpx_limit_import: 0,
about: '',
privacy_policy: '',
}) })
const errorMessages: ComputedRef<string | string[] | null> = computed( const errorMessages: ComputedRef<string | string[] | null> = computed(
() => store.getters[ROOT_STORE.GETTERS.ERROR_MESSAGES] () => store.getters[ROOT_STORE.GETTERS.ERROR_MESSAGES]
@ -150,9 +191,15 @@
? // eslint-disable-next-line @typescript-eslint/ban-ts-comment ? // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
(appData[key] = getFileSizeInMB(appConfig[key])) (appData[key] = getFileSizeInMB(appConfig[key]))
: // eslint-disable-next-line @typescript-eslint/ban-ts-comment : ['about', 'privacy_policy'].includes(key)
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
(appData[key] = appConfig[key]) ? appData[key] = appConfig[key]!== null
? appConfig[key]
: ''
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
: (appData[key] = appConfig[key])
}) })
} }
function onCancel() { function onCancel() {
@ -171,16 +218,30 @@
<style lang="scss" scoped> <style lang="scss" scoped>
@import '~@/scss/vars.scss'; @import '~@/scss/vars.scss';
.user-limit-help { #admin-app {
display: flex; .user-limit-help {
span { display: flex;
span {
font-style: italic;
}
.fa-info-circle {
margin-right: $default-margin;
}
}
.no-contact {
font-style: italic; font-style: italic;
} }
.fa-info-circle {
margin-right: $default-margin; textarea {
margin-bottom: $default-padding;
} }
} .textarea-description {
.no-contact { font-style: italic;
font-style: italic; }
.textarea-content {
margin-bottom: $default-margin;
padding: $default-padding;
}
} }
</style> </style>

View File

@ -17,13 +17,9 @@
</div> </div>
<div class="footer-item bullet"></div> <div class="footer-item bullet"></div>
<div class="footer-item"> <div class="footer-item">
<a <router-link to="/privacy-policy">
href="https://samr1.github.io/FitTrackee/" {{ $t('privacy_policy.TITLE') }}
target="_blank" </router-link>
rel="noopener noreferrer"
>
{{ $t('common.DOCUMENTATION') }}
</a>
</div> </div>
</div> </div>
</div> </div>
@ -61,6 +57,7 @@
.footer-items { .footer-items {
display: flex; display: flex;
flex-wrap: wrap;
align-content: center; align-content: center;
justify-content: center; justify-content: center;
@ -76,14 +73,17 @@
@media screen and (max-width: $x-small-limit) { @media screen and (max-width: $x-small-limit) {
.footer-items { .footer-items {
border-top: solid 1px var(--footer-border-color);
font-size: 0.85em; font-size: 0.85em;
padding: 0 0 2px;
.footer-item { .footer-item {
padding: 5px 5px; border-top: none;
padding: 1px 5px;
} }
.bullet { .bullet {
padding: 5px 0; padding: 1px 0;
} }
} }
} }

View File

@ -0,0 +1,91 @@
<template>
<div class="privacy-policy-text">
<h1>
{{ capitalize($t('privacy_policy.TITLE')) }}
</h1>
<p class="last-update">
{{ $t('privacy_policy.LAST_UPDATE')}}: {{ private_policy_date }}
</p>
<template v-if="appConfig.privacy_policy">
<div
v-html="snarkdown(linkifyAndClean(appConfig.privacy_policy))"
/>
</template>
<template v-else>
<template v-for="paragraph in paragraphs" :key="paragraph">
<h2>
{{ $t(`privacy_policy.CONTENT.${paragraph}.TITLE`)}}
</h2>
<p v-html="snarkdown($t(`privacy_policy.CONTENT.${paragraph}.CONTENT`))" />
</template>
</template>
</div>
</template>
<script lang="ts" setup>
import snarkdown from 'snarkdown'
import { ComputedRef, capitalize, computed } from 'vue'
import { AUTH_USER_STORE, ROOT_STORE } from '@/store/constants'
import { TAppConfig } from '@/types/application'
import { IAuthUserProfile } from '@/types/user'
import { useStore } from '@/use/useStore'
import { dateStringFormats, formatDate } from '@/utils/dates'
import { linkifyAndClean } from '@/utils/inputs'
const store = useStore()
const fittrackee_private_policy_date = 'Sun, 26 Feb 2023 17:00:00 GMT'
const appConfig: ComputedRef<TAppConfig> = computed(
() => store.getters[ROOT_STORE.GETTERS.APP_CONFIG]
)
const language: ComputedRef<string> = computed(
() => store.getters[ROOT_STORE.GETTERS.LANGUAGE]
)
const authUser: ComputedRef<IAuthUserProfile> = computed(
() => store.getters[AUTH_USER_STORE.GETTERS.AUTH_USER_PROFILE]
)
const dateFormat = computed(() => getDateFormat())
const timezone = computed(() => getTimezone())
const private_policy_date = computed(() => getPolicyDate())
const paragraphs = [
'DATA_COLLECTED', 'INFORMATION_USAGE', 'INFORMATION_PROTECTION',
'INFORMATION_DISCLOSURE', 'SITE_USAGE_BY_CHILDREN','ACCOUNT_DELETION',
'YOUR_CONSENT', 'CHANGES_TO_OUR_PRIVACY_POLICY'
]
function getTimezone() {
return authUser.value.timezone
? authUser.value.timezone
: Intl.DateTimeFormat().resolvedOptions().timeZone
? Intl.DateTimeFormat().resolvedOptions().timeZone
: 'Europe/Paris'
}
function getDateFormat() {
return dateStringFormats[language.value]
}
function getPolicyDate() {
return formatDate(
appConfig.value.privacy_policy && appConfig.value.privacy_policy_date
? `${appConfig.value.privacy_policy_date}`
: fittrackee_private_policy_date,
timezone.value,
dateFormat.value,
false,
)
}
</script>
<style lang="scss" scoped>
@import '~@/scss/base.scss';
.privacy-policy-text {
margin: 10px 50px 20px;
padding: $default-padding;
width: 100%;
@media screen and (max-width: $small-limit) {
margin: 0;
}
}
</style>

View File

@ -98,6 +98,27 @@
@updatePassword="updatePassword" @updatePassword="updatePassword"
@passwordError="invalidateForm" @passwordError="invalidateForm"
/> />
<label
v-if="action === 'register'"
for="accepted_policy"
class="accepted_policy"
>
<input
type="checkbox"
id="accepted_policy"
:disabled="registration_disabled"
required
@invalid="invalidateForm"
v-model="formData.accepted_policy"
/>
<span>
<i18n-t keypath="user.READ_AND_ACCEPT_PRIVACY_POLICY">
<router-link to="/privacy-policy">
{{ $t('privacy_policy.TITLE') }}
</router-link>
</i18n-t>
</span>
</label>
</div> </div>
<button <button
type="submit" type="submit"
@ -176,6 +197,7 @@
username: '', username: '',
email: '', email: '',
password: '', password: '',
accepted_policy: false
}) })
const buttonText: ComputedRef<string> = computed(() => const buttonText: ComputedRef<string> = computed(() =>
getButtonText(props.action) getButtonText(props.action)
@ -261,6 +283,7 @@
formData.username = '' formData.username = ''
formData.email = '' formData.email = ''
formData.password = '' formData.password = ''
formData.accepted_policy = false
} }
onUnmounted(() => store.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES)) onUnmounted(() => store.commit(ROOT_STORE.MUTATIONS.EMPTY_ERROR_MESSAGES))
@ -310,6 +333,12 @@
.success-message { .success-message {
margin: $default-margin; margin: $default-margin;
} }
.accepted_policy {
display: flex;
align-items: center;
font-size: .85em;
font-weight: normal;
}
} }
@media screen and (max-width: $medium-limit) { @media screen and (max-width: $medium-limit) {

View File

@ -37,7 +37,7 @@
}, },
"TITLE": "Sportarten Administration" "TITLE": "Sportarten Administration"
}, },
"UPDATE_APPLICATION_DESCRIPTION": "Aktualisiere Anwemdungskonfiguration (maximale Anzahl an registrierten Nutzern, maximale Dateigröße).", "UPDATE_APPLICATION_DESCRIPTION": "Aktualisiere Anwemdungskonfiguration.",
"UPDATE_USER_EMAIL": "Aktualisiere E-Mail", "UPDATE_USER_EMAIL": "Aktualisiere E-Mail",
"USER": "Nutzer", "USER": "Nutzer",
"USERS": { "USERS": {

View File

@ -6,6 +6,7 @@ import CommonTranslations from './common.json'
import DashboardTranslations from './dashboard.json' import DashboardTranslations from './dashboard.json'
import ErrorTranslations from './error.json' import ErrorTranslations from './error.json'
import OAuth2Translations from './oauth2.json' import OAuth2Translations from './oauth2.json'
import PrivacyPolicyTranslations from './privacy_policy.json'
import SportsTranslations from './sports.json' import SportsTranslations from './sports.json'
import StatisticsTranslations from './statistics.json' import StatisticsTranslations from './statistics.json'
import UserTranslations from './user.json' import UserTranslations from './user.json'
@ -20,6 +21,7 @@ export default {
dashboard: DashboardTranslations, dashboard: DashboardTranslations,
error: ErrorTranslations, error: ErrorTranslations,
oauth2: OAuth2Translations, oauth2: OAuth2Translations,
privacy_policy: PrivacyPolicyTranslations,
sports: SportsTranslations, sports: SportsTranslations,
statistics: StatisticsTranslations, statistics: StatisticsTranslations,
user: UserTranslations, user: UserTranslations,

View File

@ -0,0 +1 @@
{}

View File

@ -1,4 +1,5 @@
{ {
"ABOUT_THIS_INSTANCE": "About this instance",
"CONTACT_ADMIN": "Contact the administrator", "CONTACT_ADMIN": "Contact the administrator",
"FITTRACKEE_DESCRIPTION": "<strong>FitTrackee</strong> is a self-hosted outdoor activity tracker.", "FITTRACKEE_DESCRIPTION": "<strong>FitTrackee</strong> is a self-hosted outdoor activity tracker.",
"FITTRACKEE_LICENSE": "under {0} license ", "FITTRACKEE_LICENSE": "under {0} license ",

View File

@ -1,4 +1,8 @@
{ {
"ABOUT": {
"TEXT": "Detailed instance information",
"DESCRIPTION": "Any additional information that may be useful to your users. Markdown syntax can be used."
},
"ACTION": "Action", "ACTION": "Action",
"ACTIVATE_USER_ACCOUNT": "Activate account", "ACTIVATE_USER_ACCOUNT": "Activate account",
"ACTIVE": "Active", "ACTIVE": "Active",
@ -24,7 +28,9 @@
"EMAIL_SENDING_DISABLED": "Email sending is disabled.", "EMAIL_SENDING_DISABLED": "Email sending is disabled.",
"ENABLE_DISABLE_SPORTS": "Enable/disable sports.", "ENABLE_DISABLE_SPORTS": "Enable/disable sports.",
"NEW_EMAIL": "New email", "NEW_EMAIL": "New email",
"NO_TEXT_ENTERED": "No text entered",
"PASSWORD_RESET_SUCCESSFUL": "The password has been reset.", "PASSWORD_RESET_SUCCESSFUL": "The password has been reset.",
"PRIVACY_POLICY_DESCRIPTION": "Add your own privacy policy or leave blank to use the default one. Markdown syntax can be used.",
"REGISTRATION_DISABLED": "Registration is currently disabled.", "REGISTRATION_DISABLED": "Registration is currently disabled.",
"REGISTRATION_ENABLED": "Registration is currently enabled.", "REGISTRATION_ENABLED": "Registration is currently enabled.",
"RESET_USER_PASSWORD": "Reset password", "RESET_USER_PASSWORD": "Reset password",
@ -37,7 +43,7 @@
}, },
"TITLE": "Sports administration" "TITLE": "Sports administration"
}, },
"UPDATE_APPLICATION_DESCRIPTION": "Update application configuration (maximum number of registered users, maximum files size).", "UPDATE_APPLICATION_DESCRIPTION": "Update application configuration.",
"UPDATE_USER_EMAIL": "Update email", "UPDATE_USER_EMAIL": "Update email",
"USER": "user | users", "USER": "user | users",
"USERS": { "USERS": {

View File

@ -6,6 +6,7 @@ import CommonTranslations from './common.json'
import DashboardTranslations from './dashboard.json' import DashboardTranslations from './dashboard.json'
import ErrorTranslations from './error.json' import ErrorTranslations from './error.json'
import OAuth2Translations from './oauth2.json' import OAuth2Translations from './oauth2.json'
import PrivacyPolicyTranslations from './privacy_policy.json'
import SportsTranslations from './sports.json' import SportsTranslations from './sports.json'
import StatisticsTranslations from './statistics.json' import StatisticsTranslations from './statistics.json'
import UserTranslations from './user.json' import UserTranslations from './user.json'
@ -20,6 +21,7 @@ export default {
dashboard: DashboardTranslations, dashboard: DashboardTranslations,
error: ErrorTranslations, error: ErrorTranslations,
oauth2: OAuth2Translations, oauth2: OAuth2Translations,
privacy_policy: PrivacyPolicyTranslations,
sports: SportsTranslations, sports: SportsTranslations,
statistics: StatisticsTranslations, statistics: StatisticsTranslations,
user: UserTranslations, user: UserTranslations,

View File

@ -0,0 +1,38 @@
{
"CONTENT": {
"ACCOUNT_DELETION": {
"CONTENT": "You can request the deletion of your account at any time by going to this address (after logging in) and clicking on \"Delete My Account\" button in your account edition.",
"TITLE": "Account deletion"
},
"CHANGES_TO_OUR_PRIVACY_POLICY": {
"CONTENT": "If we decide to change our privacy policy, we will post those changes on this page.\n\nThis document is under [CC-BY-SA](https://creativecommons.org/licenses/by-sa/4.0/) license. Originally adapted from the [Discourse](https://github.com/discourse/discourse) privacy policy.",
"TITLE": "Changes to our Privacy Policy"
},
"DATA_COLLECTED": {
"CONTENT": "The following information are collected:\n- Account information (username, e-mail address and password). You may also enter additional profile information such as a first name, last name, birth date, location, biography and upload a profile picture.\n- [GPX](https://en.wikipedia.org/wiki/GPS_Exchange_Format) files. These files contain data related to your activities (geographic coordinates, date, distance, duration, max and average speeds, elevation, heart rate…). If you don't want to expose some data, clean them before upload or add workouts without GPX files.\n- Workout data (sport, title, date, duration, distance, ascent, descent, notes).\n- Technical information (browser name and operating system).",
"TITLE": "What information do we collect?"
},
"INFORMATION_DISCLOSURE": {
"CONTENT": "We do not sell, trade or otherwise transfer to outside parties your personally identifiable information.\n\nThis does not include trusted third parties who assist us in operating our site and servicing you, so long as those parties agree to keep this information confidential. \n\nWe may also release your information when we believe release is appropriate to comply with the law, enforce our site policies, or protect ours or others rights, property, or safety.\n\nWhen you authorize a third-party application to use your account, depending on the scope of permissions you approve, it may access your profile information or your workouts. Applications can never access your password.",
"TITLE": "Do we disclose any information to outside parties?"
},
"INFORMATION_PROTECTION": {
"CONTENT": "We implement a variety of security measures to maintain the safety of your personal information when you enter, submit, or access your personal information.",
"TITLE": "How do we protect your information?"
},
"INFORMATION_USAGE": {
"CONTENT": "Any of the information we collect from you may be used to provide the core functionality of **FitTrackee**:\n- GPX files are used to create workouts, display tracks on map (with [OpenStreetMap](https://www.openstreetmap.org) and the configured tile server) and charts, generate map thumbnails, calculate records and get weather data (if a weather provider is set).\n- Profile information and workouts are not displayed publicly. A registered user can only display his own workouts.\n- The email address you provide may be used to send you information or confirm your account modifications.",
"TITLE": "What do we use your information for?"
},
"SITE_USAGE_BY_CHILDREN": {
"CONTENT": "If this server is in the EU or the EEA: Our site and services are all directed to people who are at least 16 years old. If you are under the age of 16, per the requirements of the [GDPR](https://en.wikipedia.org/wiki/General_Data_Protection_Regulation) (General Data Protection Regulation) do not use this site.\n\nIf this server is in the USA: Our site, products and services are all directed to people who are at least 13 years old. If you are under the age of 13, per the requirements of [COPPA](https://en.wikipedia.org/wiki/Children%27s_Online_Privacy_Protection_Act) (Children's Online Privacy Protection Act) do not use this site.\n\nLaw requirements can be different if this server is in another jurisdiction.",
"TITLE": "Site usage by children"
},
"YOUR_CONSENT": {
"CONTENT": "By using our site, you consent to our web site privacy policy.",
"TITLE": "Your Consent"
}
},
"LAST_UPDATE": "Last update",
"TITLE": "privacy policy"
}

View File

@ -99,6 +99,7 @@
"METRIC": "Metric system (m, km, m/s, °C)" "METRIC": "Metric system (m, km, m/s, °C)"
} }
}, },
"READ_AND_ACCEPT_PRIVACY_POLICY": "I have read and agree to the {0}.",
"REGISTER": "Register", "REGISTER": "Register",
"REGISTER_DISABLED": "Sorry, registration is disabled.", "REGISTER_DISABLED": "Sorry, registration is disabled.",
"RESENT_ACCOUNT_CONFIRMATION": "Resend account confirmation email", "RESENT_ACCOUNT_CONFIRMATION": "Resend account confirmation email",

View File

@ -1,4 +1,5 @@
{ {
"ABOUT_THIS_INSTANCE": "A propos de cette instance",
"CONTACT_ADMIN": "Contacter l'administrateur", "CONTACT_ADMIN": "Contacter l'administrateur",
"FITTRACKEE_DESCRIPTION": "<strong>FitTrackee</strong> est un <em>tracker</em> d'activités sportives (en extérieur).", "FITTRACKEE_DESCRIPTION": "<strong>FitTrackee</strong> est un <em>tracker</em> d'activités sportives (en extérieur).",
"FITTRACKEE_LICENSE": "sous licence {0} (en) ", "FITTRACKEE_LICENSE": "sous licence {0} (en) ",

View File

@ -1,4 +1,8 @@
{ {
"ABOUT": {
"TEXT": "Information détaillée de l'instance",
"DESCRIPTION": "Toute information supplémentaire qui peut être utile à vos utilisateurs. La syntaxe Markdown peut être utilisée."
},
"ACTION": "Action", "ACTION": "Action",
"ACTIVATE_USER_ACCOUNT": "Activer le compte", "ACTIVATE_USER_ACCOUNT": "Activer le compte",
"ACTIVE": "Actif", "ACTIVE": "Actif",
@ -24,7 +28,9 @@
"EMAIL_SENDING_DISABLED": "L'envoi d'emails est désactivé.", "EMAIL_SENDING_DISABLED": "L'envoi d'emails est désactivé.",
"ENABLE_DISABLE_SPORTS": "Activer/désactiver des sports.", "ENABLE_DISABLE_SPORTS": "Activer/désactiver des sports.",
"NEW_EMAIL": "Nouvelle adresse email", "NEW_EMAIL": "Nouvelle adresse email",
"NO_TEXT_ENTERED": "pas de texte saisi",
"PASSWORD_RESET_SUCCESSFUL": "Le mot de passe a été réinitialisé.", "PASSWORD_RESET_SUCCESSFUL": "Le mot de passe a été réinitialisé.",
"PRIVACY_POLICY_DESCRIPTION": "Ajouter votre propre politique de confidentialité ou laisser vider pour utiliser la politique par défaut. La syntaxe Markdown peut être utilisée.",
"REGISTRATION_DISABLED": "Les inscriptions sont actuellement désactivées.", "REGISTRATION_DISABLED": "Les inscriptions sont actuellement désactivées.",
"REGISTRATION_ENABLED": "Les inscriptions sont actuellement activées.", "REGISTRATION_ENABLED": "Les inscriptions sont actuellement activées.",
"RESET_USER_PASSWORD": "Réinit. le mot de passe", "RESET_USER_PASSWORD": "Réinit. le mot de passe",
@ -37,7 +43,7 @@
}, },
"TITLE": "Administration - Sports" "TITLE": "Administration - Sports"
}, },
"UPDATE_APPLICATION_DESCRIPTION": "Configurer l'application (nombre maximum d'utilisateurs inscrits, taille maximale des fichers).", "UPDATE_APPLICATION_DESCRIPTION": "Configurer l'application.",
"UPDATE_USER_EMAIL": "Changer l'email", "UPDATE_USER_EMAIL": "Changer l'email",
"USER": "utilisateur | utilisateurs", "USER": "utilisateur | utilisateurs",
"USERS": { "USERS": {

View File

@ -6,6 +6,7 @@ import CommonTranslations from './common.json'
import DashboardTranslations from './dashboard.json' import DashboardTranslations from './dashboard.json'
import ErrorTranslations from './error.json' import ErrorTranslations from './error.json'
import OAuth2Translations from './oauth2.json' import OAuth2Translations from './oauth2.json'
import PrivacyPolicyTranslations from './privacy_policy.json'
import SportsTranslations from './sports.json' import SportsTranslations from './sports.json'
import StatisticsTranslations from './statistics.json' import StatisticsTranslations from './statistics.json'
import UserTranslations from './user.json' import UserTranslations from './user.json'
@ -20,6 +21,7 @@ export default {
dashboard: DashboardTranslations, dashboard: DashboardTranslations,
error: ErrorTranslations, error: ErrorTranslations,
oauth2: OAuth2Translations, oauth2: OAuth2Translations,
privacy_policy: PrivacyPolicyTranslations,
sports: SportsTranslations, sports: SportsTranslations,
statistics: StatisticsTranslations, statistics: StatisticsTranslations,
user: UserTranslations, user: UserTranslations,

View File

@ -0,0 +1,38 @@
{
"CONTENT": {
"ACCOUNT_DELETION": {
"CONTENT": "Vous pouvez demander à tout moment la suppression de votre compte en vous rendant à cette adresse (après vous être connecté à votre compte), puis en cliquant sur le bouton sous \"Supprimer mon compte\" dans l'espace de mise à jour de votre compte.",
"TITLE": "Suppression du compte"
},
"CHANGES_TO_OUR_PRIVACY_POLICY": {
"CONTENT": "Si nous décidons de changer notre politique de confidentialité, nous afficherons ces modifications sur cette page.\n\nCe document est sous licence [CC-BY-SA](https://creativecommons.org/licenses/by-sa/4.0/). Adaptée de la politique de confidentialité de [Discourse](https://github.com/discourse/discourse).",
"TITLE": "Changes to our Privacy Policy"
},
"DATA_COLLECTED": {
"CONTENT": "Les informations suivantes sont collectées :\n- Informations liées au compte (nom d'utilisateur, courriel et mot de passe). Vous pouvez également saisir les informations du profil tel que le prénom, le nom de famille, la date de naissance, la localisation, une biographie et envoyer une image de profil.\n- Fichiers [GPX](https://fr.wikipedia.org/wiki/GPX_(format_de_fichier). Ces fichiers contiennent les données liées à vos activités (coordonnées géographiques, date, distance, durée, vitesses maximale et moyenne, altitude, rythme cardiaque…). Si vous ne souhaitez pas exposer certaines données, nettoyer les fichiers avant de les envoyer ou ajouter des activités sans fichier GPX.\n- Données d'activités (sport, titre, date, durée, distance, dénivelé positif et négatif, notes).\n- Données techniques (nom du navigateur et du système d'exploitation).",
"TITLE": "Quelles sont les informations que nous recueillons ?"
},
"INFORMATION_DISCLOSURE": {
"CONTENT": "Nous ne vendons pas, ni échangeons ou même transférons vos renseignements personnelles à des tiers.\n\nCeci ninclut pas les tiers de confiance qui nous aident à exploiter notre site ou vous servir, tant que ces parties conviennent à garder ces informations confidentielles.\n\nNous pouvons également divulguer vos informations lorsque nous croyons nécessaire de se conformer à la loi, appliquer nos politiques de site, ou la nôtre ou dautres droits, la propriété ou la sécurité.\n\nSi vous autorisez une application tierce à utiliser votre compte, selon le périmètre des permissions accordées, elle pourra avoir accès à vos informations de profil ou vos activités. Les applications tierces ne peuvent jamais accéder à votre mot de passe.",
"TITLE": "Divulguons-nous des informations à des tiers ?"
},
"INFORMATION_PROTECTION": {
"CONTENT": "Nous mettons en œuvre une variété de mesures de sécurité pour maintenir la sécurité de vos informations personnelles lorsque vous saisissez, soumettez ou daccédez à vos renseignements personnels.",
"TITLE": "Comment protégeons-nous vos informations ?"
},
"INFORMATION_USAGE": {
"CONTENT": "Toutes les informations que nous recueillons auprès de vous peuvent être utilisées afin de fournir les fonctionnalités de **FitTrackee** :\n- Les fichiers GPX sont utilisés pour créer des activités, afficher des traces sur une carte (avec [OpenStreetMap](https://www.openstreetmap.org) et le serveur de tuiles configuré) et des graphiques, générer des vignettes de cartes, calculer des records et obtenir des données météo (si un fournisseur de données météorologiques est configuré).\n- Les informations du profil et les activités ne sont pas affichées publiquement. Un utilisateur enregistré ne peut voir que ses propres activités.\n- Le courriel que vous avez fourni peut être utilisé pour vous envoyer des informations ou confirmer des actions de modification de votre compte.",
"TITLE": "Comment utilisons-nous vos informations ?"
},
"SITE_USAGE_BY_CHILDREN": {
"CONTENT": "Si ce serveur est localisé dans l'Union Européenne (UE) ou l'Espace Economique Européen (EEA) : notre site et nos services sont tous destinés aux personnes âgées d'au moins 16 ans. Si vous avez moins de 16 ans, conformément aux exigences du [RGPD](https://fr.wikipedia.org/wiki/R%C3%A8glement_g%C3%A9n%C3%A9ral_sur_la_protection_des_donn%C3%A9es) (Règlement général sur la protection des données), n'utilisez pas ce site.\n\nSi ce serveur se trouve aux États-Unis : notre site et nos services sont tous destinés à des personnes âgées d'au moins 13 ans. Si vous avez moins de 13 ans, conformément aux exigences de la loi [COPPA](https://fr.wikipedia.org/wiki/Children%27s_Online_Privacy_Protection_Act) (Children's Online Privacy Protection Act), n'utilisez pas ce site.\n\nLes exigences légales peuvent être différentes si ce serveur se trouve dans une autre juridiction.",
"TITLE": "Protection des mineurs"
},
"YOUR_CONSENT": {
"CONTENT": "En utilisant notre site, vous acceptez la politique de confidentialité de notre site web.",
"TITLE": "Votre consentement"
}
},
"LAST_UPDATE": "Dernière mise à jour",
"TITLE": "politique de confidentialité"
}

View File

@ -99,6 +99,7 @@
"METRIC": "Système métrique (m, km, m/s, °C)" "METRIC": "Système métrique (m, km, m/s, °C)"
} }
}, },
"READ_AND_ACCEPT_PRIVACY_POLICY": "J'ai lu et accepte la {0}.",
"REGISTER": "S'inscrire", "REGISTER": "S'inscrire",
"REGISTER_DISABLED": "Désolé, les inscriptions sont désactivées.", "REGISTER_DISABLED": "Désolé, les inscriptions sont désactivées.",
"RESENT_ACCOUNT_CONFIRMATION": "Envoyer à nouveau le courriel de confirmation de compte", "RESENT_ACCOUNT_CONFIRMATION": "Envoyer à nouveau le courriel de confirmation de compte",

View File

@ -37,7 +37,7 @@
}, },
"TITLE": "Amministrazione sport" "TITLE": "Amministrazione sport"
}, },
"UPDATE_APPLICATION_DESCRIPTION": "Aggiorna configurazione applicazione (numero massimo di utenti registrati, dimensione massima dei files).", "UPDATE_APPLICATION_DESCRIPTION": "Aggiorna configurazione applicazione.",
"UPDATE_USER_EMAIL": "Aggiorna email", "UPDATE_USER_EMAIL": "Aggiorna email",
"USER": "utente | utenti", "USER": "utente | utenti",
"USERS": { "USERS": {

View File

@ -6,6 +6,7 @@ import CommonTranslations from './common.json'
import DashboardTranslations from './dashboard.json' import DashboardTranslations from './dashboard.json'
import ErrorTranslations from './error.json' import ErrorTranslations from './error.json'
import OAuth2Translations from './oauth2.json' import OAuth2Translations from './oauth2.json'
import PrivacyPolicyTranslations from './privacy_policy.json'
import SportsTranslations from './sports.json' import SportsTranslations from './sports.json'
import StatisticsTranslations from './statistics.json' import StatisticsTranslations from './statistics.json'
import UserTranslations from './user.json' import UserTranslations from './user.json'
@ -20,6 +21,7 @@ export default {
dashboard: DashboardTranslations, dashboard: DashboardTranslations,
error: ErrorTranslations, error: ErrorTranslations,
oauth2: OAuth2Translations, oauth2: OAuth2Translations,
privacy_policy: PrivacyPolicyTranslations,
sports: SportsTranslations, sports: SportsTranslations,
statistics: StatisticsTranslations, statistics: StatisticsTranslations,
user: UserTranslations, user: UserTranslations,

View File

@ -0,0 +1 @@
{}

View File

@ -6,6 +6,7 @@ import CommonTranslations from './common.json'
import DashboardTranslations from './dashboard.json' import DashboardTranslations from './dashboard.json'
import ErrorTranslations from './error.json' import ErrorTranslations from './error.json'
import OAuth2Translations from './oauth2.json' import OAuth2Translations from './oauth2.json'
import PrivacyPolicyTranslations from './privacy_policy.json'
import SportsTranslations from './sports.json' import SportsTranslations from './sports.json'
import StatisticsTranslations from './statistics.json' import StatisticsTranslations from './statistics.json'
import UserTranslations from './user.json' import UserTranslations from './user.json'
@ -20,6 +21,7 @@ export default {
dashboard: DashboardTranslations, dashboard: DashboardTranslations,
error: ErrorTranslations, error: ErrorTranslations,
oauth2: OAuth2Translations, oauth2: OAuth2Translations,
privacy_policy: PrivacyPolicyTranslations,
sports: SportsTranslations, sports: SportsTranslations,
statistics: StatisticsTranslations, statistics: StatisticsTranslations,
user: UserTranslations, user: UserTranslations,

View File

@ -0,0 +1 @@
{}

View File

@ -37,7 +37,7 @@
}, },
"TITLE": "Beheer sporten" "TITLE": "Beheer sporten"
}, },
"UPDATE_APPLICATION_DESCRIPTION": "De applicatie configureren (maximum aantal geregistreerde gebruikers, maximum bestandsgrootte).", "UPDATE_APPLICATION_DESCRIPTION": "De applicatie configureren.",
"UPDATE_USER_EMAIL": "Update email", "UPDATE_USER_EMAIL": "Update email",
"USER": "gebruiker | gebruikers", "USER": "gebruiker | gebruikers",
"USERS": { "USERS": {

View File

@ -6,6 +6,7 @@ import CommonTranslations from './common.json'
import DashboardTranslations from './dashboard.json' import DashboardTranslations from './dashboard.json'
import ErrorTranslations from './error.json' import ErrorTranslations from './error.json'
import OAuth2Translations from './oauth2.json' import OAuth2Translations from './oauth2.json'
import PrivacyPolicyTranslations from './privacy_policy.json'
import SportsTranslations from './sports.json' import SportsTranslations from './sports.json'
import StatisticsTranslations from './statistics.json' import StatisticsTranslations from './statistics.json'
import UserTranslations from './user.json' import UserTranslations from './user.json'
@ -20,6 +21,7 @@ export default {
dashboard: DashboardTranslations, dashboard: DashboardTranslations,
error: ErrorTranslations, error: ErrorTranslations,
oauth2: OAuth2Translations, oauth2: OAuth2Translations,
privacy_policy: PrivacyPolicyTranslations,
sports: SportsTranslations, sports: SportsTranslations,
statistics: StatisticsTranslations, statistics: StatisticsTranslations,
user: UserTranslations, user: UserTranslations,

View File

@ -0,0 +1 @@
{}

View File

@ -23,6 +23,7 @@ import { AUTH_USER_STORE } from '@/store/constants'
import AboutView from '@/views/AboutView.vue' import AboutView from '@/views/AboutView.vue'
import Dashboard from '@/views/Dashboard.vue' import Dashboard from '@/views/Dashboard.vue'
import NotFoundView from '@/views/NotFoundView.vue' import NotFoundView from '@/views/NotFoundView.vue'
import PrivacyPolicyView from '@/views/PrivacyPolicyView.vue'
import LoginOrRegister from '@/views/user/LoginOrRegister.vue' import LoginOrRegister from '@/views/user/LoginOrRegister.vue'
const getTabFromPath = (path: string): string => { const getTabFromPath = (path: string): string => {
@ -318,6 +319,11 @@ const routes: Array<RouteRecordRaw> = [
name: 'About', name: 'About',
component: AboutView, component: AboutView,
}, },
{
path: '/privacy-policy',
name: 'PrivacyPolicy',
component: PrivacyPolicyView,
},
{ {
path: '/:pathMatch(.*)*', path: '/:pathMatch(.*)*',
name: 'not-found', name: 'not-found',
@ -342,7 +348,7 @@ const pathsWithoutAuthentication = [
'/account-confirmation/email-sent', '/account-confirmation/email-sent',
] ]
const pathsWithoutChecks = ['/email-update', '/about'] const pathsWithoutChecks = ['/email-update', '/about', '/privacy-policy']
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
store store

View File

@ -7,6 +7,7 @@ export interface IAppStatistics {
export type TAppConfig = { export type TAppConfig = {
[key: string]: number | boolean | string | null [key: string]: number | boolean | string | null
about: string | null
admin_contact: string admin_contact: string
gpx_limit_import: number gpx_limit_import: number
is_email_sending_enabled: boolean is_email_sending_enabled: boolean
@ -15,6 +16,8 @@ export type TAppConfig = {
max_single_file_size: number max_single_file_size: number
max_users: number max_users: number
max_zip_file_size: number max_zip_file_size: number
privacy_policy: string | null
privacy_policy_date: string | null
version: string version: string
weather_provider: string | null weather_provider: string | null
} }
@ -26,9 +29,11 @@ export interface IApplication {
export type TAppConfigForm = { export type TAppConfigForm = {
[key: string]: number | string [key: string]: number | string
about: string
admin_contact: string admin_contact: string
gpx_limit_import: number gpx_limit_import: number
max_single_file_size: number max_single_file_size: number
max_users: number max_users: number
max_zip_file_size: number max_zip_file_size: number
privacy_policy: string
} }

View File

@ -99,6 +99,7 @@ export interface ILoginRegisterFormData {
email: string email: string
password: string password: string
language?: string language?: string
accepted_policy?: boolean
} }
export interface ILoginOrRegisterData { export interface ILoginOrRegisterData {

View File

@ -93,7 +93,7 @@ const availableDateFormats = [
'yyyy-MM-dd', 'yyyy-MM-dd',
'date_string', 'date_string',
] ]
const dateStringFormats: Record<string, string> = { export const dateStringFormats: Record<string, string> = {
de: 'do MMM yyyy', de: 'do MMM yyyy',
en: 'MMM. do, yyyy', en: 'MMM. do, yyyy',
fr: 'd MMM yyyy', fr: 'd MMM yyyy',

View File

@ -0,0 +1,12 @@
<template>
<div id="privacy-policy" class="view">
<div class="container">
<PrivacyPolicy />
</div>
<div id="bottom" />
</div>
</template>
<script lang="ts" setup>
import PrivacyPolicy from '@/components/PrivacyPolicy.vue'
</script>

View File

@ -7230,6 +7230,11 @@ slash@^4.0.0:
resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7"
integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==
snarkdown@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/snarkdown/-/snarkdown-2.0.0.tgz#b1feb4db91b9f94a8ebbd7a50f3e99aee18b1e03"
integrity sha512-MgL/7k/AZdXCTJiNgrO7chgDqaB9FGM/1Tvlcenenb7div6obaDATzs16JhFyHHBGodHT3B7RzRc5qk8pFhg3A==
sockjs@^0.3.24: sockjs@^0.3.24:
version "0.3.24" version "0.3.24"
resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce"