Add requesting user data

This commit is contained in:
kolaente 2021-09-04 20:02:28 +02:00
parent ed9200b9ae
commit ef5be83064
Signed by: konrad
GPG Key ID: F40E70337AB24C9B
7 changed files with 119 additions and 15 deletions

View File

@ -0,0 +1,71 @@
<template>
<card :title="$t('user.export.title')">
<p>
{{ $t('user.export.description') }}
</p>
<p>
{{ $t('user.export.descriptionPasswordRequired') }}
</p>
<div class="field">
<label class="label" for="currentPasswordDataExport">
{{ $t('user.settings.currentPassword') }}
</label>
<div class="control">
<input
class="input"
:class="{'is-danger': errPasswordRequired}"
id="currentPasswordDataExport"
:placeholder="$t('user.settings.currentPasswordPlaceholder')"
type="password"
v-model="password"
@keyup="() => errPasswordRequired = password === ''"
ref="passwordInput"
/>
</div>
<p class="help is-danger" v-if="errPasswordRequired">
{{ $t('user.deletion.passwordRequired') }}
</p>
</div>
<x-button
:loading="dataExportService.loading"
@click="requestDataExport()"
class="is-fullwidth mt-4">
{{ $t('user.export.request') }}
</x-button>
</card>
</template>
<script>
import DataExportService from '../../../services/dataExport'
export default {
name: 'data-export',
data() {
return {
dataExportService: DataExportService,
password: '',
errPasswordRequired: false,
}
},
created() {
this.dataExportService = new DataExportService()
},
methods: {
requestDataExport() {
if (this.password === '') {
this.errPasswordRequired = true
this.$refs.passwordInput.focus()
return
}
this.dataExportService.request(this.password)
.then(() => {
this.success({message: this.$t('user.export.success')})
this.password = ''
})
.catch(e => this.error(e))
},
},
}
</script>

View File

@ -0,0 +1,7 @@
export const downloadBlob = (url: string, filename: string) => {
const link = document.createElement('a')
link.href = url
link.setAttribute('download', filename)
link.click()
window.URL.revokeObjectURL(url)
}

View File

@ -111,6 +111,13 @@
"scheduledCancelText": "To cancel the deletion of your account, please enter your password below:",
"scheduledCancelConfirm": "Cancel the deletion of my account",
"scheduledCancelSuccess": "We will not delete your account."
},
"export": {
"title": "Export your Vikunja Data",
"description": "You can request a copy of all your Vikunja data. This include Namespaces, Lists, Tasks and everything associated to them. You can import this data in any Vikunja instance through the migration function.",
"descriptionPasswordRequired": "Please enter your password to proceed:",
"request": "Request a copy of my Vikunja Data",
"success": "You've successfully requested your Vikunja Data! We will send you an email once it's ready to download."
}
},
"list": {

View File

@ -319,6 +319,17 @@ export default class AbstractService {
})
}
getBlobUrl(url, method = 'GET', data = {}) {
return this.http({
url: url,
method: method,
responseType: 'blob',
data: data,
}).then(response => {
return window.URL.createObjectURL(new Blob([response.data]))
})
}
/**
* Performs a get request to the url specified before.
* The difference between this and get() is this one is used to get a bunch of data (an array), not just a single object.
@ -488,7 +499,7 @@ export default class AbstractService {
*/
uploadFormData(url, formData) {
console.log(formData, formData._boundary)
const cancel = this.setLoading()
return this.http.put(
url,

View File

@ -1,6 +1,7 @@
import AbstractService from './abstractService'
import AttachmentModel from '../models/attachment'
import {formatISO} from 'date-fns'
import {downloadBlob} from '@/helpers/downloadBlob'
export default class AttachmentService extends AbstractService {
constructor() {
@ -33,23 +34,12 @@ export default class AttachmentService extends AbstractService {
}
getBlobUrl(model) {
return this.http({
url: '/tasks/' + model.taskId + '/attachments/' + model.id,
method: 'GET',
responseType: 'blob',
}).then(response => {
return window.URL.createObjectURL(new Blob([response.data]))
})
return AbstractService.prototype.getBlobUrl.call(this, '/tasks/' + model.taskId + '/attachments/' + model.id)
}
download(model) {
this.getBlobUrl(model).then(url => {
const link = document.createElement('a')
link.href = url
link.setAttribute('download', model.file.name)
link.click()
window.URL.revokeObjectURL(url)
})
this.getBlobUrl(model)
.then(url => downloadBlob(url, model.file.name))
}
/**

View File

@ -0,0 +1,13 @@
import AbstractService from './abstractService'
import {downloadBlob} from '../helpers/downloadBlob'
export default class DataExportService extends AbstractService {
request(password) {
return this.post('/user/export/request', {password: password})
}
download(password) {
this.getBlobUrl('/user/export/download', 'POST', {password})
.then(url => downloadBlob(url, 'vikunja-export.zip'))
}
}

View File

@ -235,6 +235,9 @@
</div>
</template>
</card>
<!-- Data export -->
<data-export/>
<!-- Migration -->
<card :title="$t('migrate.title')" v-if="migratorsEnabled">
@ -293,6 +296,7 @@ import AvatarSettings from '../../components/user/avatar-settings.vue'
import copy from 'copy-to-clipboard'
import ListSearch from '@/components/tasks/partials/listSearch.vue'
import UserSettingsDeletion from '../../components/user/settings/deletion'
import DataExport from '../../components/user/settings/data-export'
export default {
name: 'Settings',
@ -325,6 +329,7 @@ export default {
UserSettingsDeletion,
ListSearch,
AvatarSettings,
DataExport,
},
created() {
this.passwordUpdateService = new PasswordUpdateService()