Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/privacy.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGeorg Ehrke <developer@georgehrke.com>2019-11-27 09:47:47 +0300
committerGeorg Ehrke <developer@georgehrke.com>2020-04-17 11:18:18 +0300
commitc0615e3a55885e341bb82e2f8fdb3b2f8e6f49c9 (patch)
treed1c142a6c99b3ca5409862a2c95769dc72aa0dfb /src
parent3f3f4af7cbb677abd151e2912cbb99fe81bebcd2 (diff)
Move to new nextcloud npm packages
Signed-off-by: Georg Ehrke <developer@georgehrke.com>
Diffstat (limited to 'src')
-rw-r--r--src/Admins.vue105
-rw-r--r--src/Encryption.vue93
-rw-r--r--src/Location.vue122
-rw-r--r--src/Shares.vue66
-rw-r--r--src/components/Admins.vue165
-rw-r--r--src/components/Encryption.vue137
-rw-r--r--src/components/Location.vue147
-rw-r--r--src/components/Map.vue (renamed from src/Map.vue)0
-rw-r--r--src/components/Shares.vue101
-rw-r--r--src/main.js42
-rw-r--r--src/nameProvider.js2
11 files changed, 571 insertions, 409 deletions
diff --git a/src/Admins.vue b/src/Admins.vue
deleted file mode 100644
index b3c57fb..0000000
--- a/src/Admins.vue
+++ /dev/null
@@ -1,105 +0,0 @@
-<template>
- <div class="who-has-access">
- <span :class="{ hidden: !isLoading }" class="icon icon-loading" />
- <div v-for="admin in admins" :key="admin.id" class="admin-avatar-container">
- <Avatar :user="admin.internal ? admin.id : null"
- :display-name="admin.displayname" :size="64" :is-no-user="!admin.internal" />
- <Actions v-if="!admin.internal">
- <ActionButton icon="icon-close" @click="deleteAdditionalAdmin(admin)">
- {{ t('privacy', 'Remove external admin') }}
- </ActionButton>
- </Actions>
- </div>
-
- <div v-if="isAdmin">
- <Actions v-if="!isAdding" class="addAdditionalAdmin">
- <ActionButton icon="icon-add" @click="openNewAdmin">
- {{ t('privacy', 'Add external admin') }}
- </ActionButton>
- </Actions>
- <form v-if="isAdding"
- v-click-outside="closeNewAdmin"
- class="addAdditionalAdminFormContainer"
- @submit.prevent="addAdditionalAdmin">
- <input v-model="newAdditionalAdminInputField" type="text" maxlength="64"
- autocomplete="new-password" autocorrect="off" autocapitalize="off"
- spellcheck="false" :placeholder="additionalAdminPlaceholderLabel">
- <input type="submit" value="" class="icon-confirm">
- <!-- add icon-loading -->
- </form>
- </div>
- </div>
-</template>
-
-<script>
-import ClickOutside from 'vue-click-outside'
-import HttpClient from 'nextcloud-axios'
-import Vue from 'vue'
-
-import Actions from 'nextcloud-vue/dist/Components/Actions'
-import ActionButton from 'nextcloud-vue/dist/Components/ActionButton'
-import Avatar from 'nextcloud-vue/dist/Components/Avatar'
-import { generateUrl } from 'nextcloud-server/dist/router'
-
-export default {
- name: 'Admins',
- components: {
- Actions,
- ActionButton,
- Avatar
- },
- directives: {
- ClickOutside
- },
- data() {
- return {
- admins: [],
- newAdditionalAdminInputField: '',
- isAdmin: false,
- isLoading: true,
- isAdding: false,
- isSavingChanges: false
- }
- },
- mounted() {
- this.isAdmin = OC.isUserAdmin()
-
- const url = generateUrl('/apps/privacy/api/admins')
- HttpClient.get(url).then(resp => {
- Vue.set(this, 'admins', resp.data)
- this.isLoading = false
- })
- },
- methods: {
- openNewAdmin() {
- setTimeout(() => {
- this.isAdding = true
- }, 0)
- },
- closeNewAdmin() {
- this.isAdding = false
- this.newAdditionalAdminInputField = ''
- },
- addAdditionalAdmin() {
- console.warn(this.newAdditionalAdminInputField)
- const url = generateUrl('/apps/privacy/api/admins')
- this.isSavingChanges = true
-
- HttpClient.post(url, { name: this.newAdditionalAdminInputField }).then(resp => {
- this.admins.push(resp.data)
-
- this.isSavingChanges = false
- this.isAdding = false
- this.newAdditionalAdminInputField = ''
- })
- },
- deleteAdditionalAdmin(admin) {
- const url = generateUrl('/apps/privacy/api/admins/{id}', { id: admin.id })
- HttpClient.delete(url).then(resp => {
- const index = this.admins.indexOf(admin)
- this.admins.splice(index, 1)
- })
- }
- }
-}
-</script>
diff --git a/src/Encryption.vue b/src/Encryption.vue
deleted file mode 100644
index 69b330a..0000000
--- a/src/Encryption.vue
+++ /dev/null
@@ -1,93 +0,0 @@
-<template>
- <div class="who-has-access">
- <!-- eslint-disable-next-line vue/no-v-html -->
- <p v-show="!isEditing" v-html="label" />
- <Actions v-if="isAdmin && !isEditing">
- <ActionButton icon="icon-rename" @click="openEditFullDiskEncryptionForm" />
- </Actions>
- <div v-if="isEditing" v-click-outside="cancelEditFullDiskEncryptionForm">
- <form>
- <input id="fullDiskEncryptionEnabledCheckbox" v-model="fullDiskEncryptionEnabled"
- :disabled="isSavingChanges" type="checkbox" name="fullDiskEncryptionEnabledCheckbox"
- class="checkbox" @change="saveFullDiskEncryptionForm">
- <label for="fullDiskEncryptionEnabledCheckbox">
- {{ checkboxLabel }}
- </label>
- </form>
- </div>
- </div>
-</template>
-
-<script>
-import HttpClient from 'nextcloud-axios'
-import ClickOutside from 'vue-click-outside'
-
-import { generateUrl } from 'nextcloud-server/dist/router'
-
-import Actions from 'nextcloud-vue/dist/Components/Actions'
-import ActionButton from 'nextcloud-vue/dist/Components/ActionButton'
-
-export default {
- name: 'Encryption',
- components: {
- Actions,
- ActionButton
- },
- directives: {
- ClickOutside
- },
- data() {
- return {
- fullDiskEncryptionEnabled: false,
- serverSideEncryptionEnabled: false,
- isAdmin: true,
- isEditing: false,
- isSavingChanges: false
- }
- },
- computed: {
- label() {
- if (!this.serverSideEncryptionEnabled && !this.fullDiskEncryptionEnabled) {
- return t('privacy', 'Your files are not protected by encryption.')
- } else if (this.serverSideEncryptionEnabled && !this.fullDiskEncryptionEnabled) {
- return t('privacy', 'Your files are encrypted with {linkopen}server-side-encryption ↗{linkclose}.')
- .replace('{linkopen}', '<a href="https://nextcloud.com/blog/encryption-in-nextcloud/" target="_blank" title="" rel="noreferrer noopener">')
- .replace('{linkclose}', '</a>')
- } else if (!this.serverSideEncryptionEnabled && this.fullDiskEncryptionEnabled) {
- return t('privacy', 'This server is protected with full-disk-encryption.')
- } else {
- return t('privacy', 'Your files are encrypted with {linkopen}server-side-encryption ↗{linkclose}. Additionally, this server is protected with full-disk-encryption.')
- .replace('{linkopen}', '<a href="https://nextcloud.com/blog/encryption-in-nextcloud/" target="_blank" title="" rel="noreferrer noopener">')
- .replace('{linkclose}', '</a>')
- }
- },
- checkboxLabel() {
- return t('privacy', 'This server is using full-disk-encryption.')
- }
- },
- created() {
- this.fullDiskEncryptionEnabled = this.$parent.fullDiskEncryptionEnabled
- this.serverSideEncryptionEnabled = this.$parent.serverSideEncryptionEnabled
- this.isAdmin = OC.isUserAdmin()
- },
- methods: {
- openEditFullDiskEncryptionForm() {
- setTimeout(() => {
- this.isEditing = true
- }, 0)
- },
- cancelEditFullDiskEncryptionForm() {
- this.isEditing = false
- },
- saveFullDiskEncryptionForm() {
- const url = generateUrl('/apps/privacy/api/fullDiskEncryption')
- this.isSavingChanges = true
-
- HttpClient.post(url, { enabled: this.fullDiskEncryptionEnabled ? '1' : '0' }).then(resp => {
- this.isSavingChanges = false
- this.isEditing = false
- })
- }
- }
-}
-</script>
diff --git a/src/Location.vue b/src/Location.vue
deleted file mode 100644
index 47c5b54..0000000
--- a/src/Location.vue
+++ /dev/null
@@ -1,122 +0,0 @@
-<template>
- <div class="where-is-my-data">
- <span v-show="isLoading" class="icon icon-loading" />
- <p v-show="!isEditingLocation && !isLoading">
- <span v-show="country">{{ label }}<strong>{{ country }}.</strong></span>
- <span v-show="!country">{{ labelForNoCountry }}</span>
- <Actions v-if="isAdmin">
- <ActionButton icon="icon-rename" @click="editLocation">
- {{ t('privacy', 'Change data location') }}
- </ActionButton>
- </Actions>
- </p>
- <div v-show="isEditingLocation && !isLoading" class="multiselect-container">
- <Multiselect
- :disabled="isSavingChanges"
- :options="options"
- :searchable="true"
- track-by="code"
- label="label"
- :placeholder="placeholderLabel"
- @input="onChange" />
- <span v-show="isSavingChanges" class="icon icon-loading" />
- </div>
- <Map v-show="!isLoading" />
- </div>
-</template>
-
-<script>
-import Map from './Map.vue'
-import HttpClient from 'nextcloud-axios'
-import { generateUrl } from 'nextcloud-server/dist/router'
-
-import Multiselect from 'nextcloud-vue/dist/Components/Multiselect'
-import Actions from 'nextcloud-vue/dist/Components/Actions'
-import ActionButton from 'nextcloud-vue/dist/Components/ActionButton'
-import {
- getCountryList,
- getNameForCountryCode
-} from './nameProvider.js'
-
-export default {
- name: 'Location',
- components: {
- ActionButton,
- Actions,
- Map,
- Multiselect
- },
- data() {
- return {
- selectedCountry: 'de',
- isAdmin: false,
- isEditingLocation: false,
- isLoading: true,
- isSavingChanges: false
- }
- },
- computed: {
- label() {
- return t('privacy', 'Your data is located in: ')
- },
- labelForNoCountry() {
- return t('privacy', 'The admin hasn\'t selected the location of the server yet.')
- },
- country() {
- return getNameForCountryCode(this.$data.selectedCountry)
- },
- options() {
- return getCountryList()
- },
- placeholderLabel() {
- return t('privacy', 'Please select a country')
- }
- },
- watch: {
- selectedCountry: (newCountry, oldCountry) => {
- const oldElm = document.querySelector('.where-is-my-data #' + oldCountry)
- const newElm = document.querySelector('.where-is-my-data #' + newCountry)
-
- if (oldElm) {
- oldElm.style.fill = null
- }
- if (newElm) {
- newElm.style.fill = 'var(--color-primary)'
- }
- }
- },
- mounted() {
- this.isAdmin = OC.isUserAdmin()
- const url = generateUrl('/apps/privacy/api/location')
-
- HttpClient.get(url).then(resp => {
- this.selectedCountry = resp.data.code
-
- if (this.selectedCountry !== '') {
- const elm = document.querySelector('.where-is-my-data #' + this.selectedCountry)
- if (elm) {
- elm.style.fill = '#e6605c'
- }
- }
-
- this.isLoading = false
- })
- },
- methods: {
- editLocation() {
- this.isEditingLocation = true
- },
- onChange(value) {
- const url = generateUrl('/apps/privacy/api/location')
- this.isSavingChanges = true
-
- HttpClient.post(url, { code: value.code }).then(resp => {
- this.selectedCountry = value.code
-
- this.isEditingLocation = false
- this.isSavingChanges = false
- })
- }
- }
-}
-</script>
diff --git a/src/Shares.vue b/src/Shares.vue
deleted file mode 100644
index 5693a28..0000000
--- a/src/Shares.vue
+++ /dev/null
@@ -1,66 +0,0 @@
-<template>
- <div class="who-has-access">
- <span :class="{hidden: !isLoading}" class="icon icon-loading" />
- <span :class="{hidden: !isEmptyList}">
- {{ emptyLabel }}
- </span>
- <Avatar v-for="uid in uniqueShareUIDs" :key="uid" :user="uid"
- :display-name="uidDisplaynameMap[uid]" :size="64" />
- </div>
-</template>
-
-<script>
-import HttpClient from 'nextcloud-axios'
-import Vue from 'vue'
-
-import { generateOcsUrl } from 'nextcloud-server/dist/router'
-import Avatar from 'nextcloud-vue/dist/Components/Avatar'
-
-export default {
- name: 'Shares',
- components: {
- Avatar
- },
- data() {
- return {
- uniqueShareUIDs: [],
- uidDisplaynameMap: {},
- isLoading: true
- }
- },
- computed: {
- isEmptyList() {
- return this.isLoading === false && this.uniqueShareUIDs.length === 0
- },
- emptyLabel() {
- return t('privacy', 'You don\'t have any shares with individual users.')
- }
- },
- mounted: function() {
- const url = generateOcsUrl('/apps/files_sharing/api/v1/shares?format=json&shared_with_me=false')
- const currentUserId = OC.getCurrentUser()
-
- HttpClient.get(url).then(resp => {
- resp.data.ocs.data.forEach((d) => {
- if (d.share_with === currentUserId) {
- return
- }
-
- switch (d.share_type) {
- case 0:
- if (this.uniqueShareUIDs.indexOf(d.share_with) === -1) {
- this.uniqueShareUIDs.push(d.share_with)
- Vue.set(this.uidDisplaynameMap, d.share_with, d.share_with_displayname)
- }
- break
-
- default:
- break
- }
- })
-
- this.isLoading = false
- })
- }
-}
-</script>
diff --git a/src/components/Admins.vue b/src/components/Admins.vue
new file mode 100644
index 0000000..0765dcb
--- /dev/null
+++ b/src/components/Admins.vue
@@ -0,0 +1,165 @@
+<!--
+ - @copyright Copyright (c) 2019 Georg Ehrke <oc.list@georgehrke.com>
+ - @author Georg Ehrke <oc.list@georgehrke.com>
+ -
+ - @license GNU AGPL version 3 or any later version
+ -
+ - This program is free software: you can redistribute it and/or modify
+ - it under the terms of the GNU Affero General Public License as
+ - published by the Free Software Foundation, either version 3 of the
+ - License, or (at your option) any later version.
+ -
+ - This program is distributed in the hope that it will be useful,
+ - but WITHOUT ANY WARRANTY; without even the implied warranty of
+ - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ - GNU Affero General Public License for more details.
+ -
+ - You should have received a copy of the GNU Affero General Public License
+ - along with this program. If not, see <http://www.gnu.org/licenses/>.
+ -
+ -->
+
+<template>
+ <div class="who-has-access">
+ <span :class="{ hidden: !isLoading }" class="icon icon-loading" />
+ <div v-for="admin in admins" :key="admin.id" class="admin-avatar-container">
+ <Avatar :user="admin.internal ? admin.id : null"
+ :display-name="admin.displayname"
+ :size="64"
+ :is-no-user="!admin.internal" />
+ <Actions v-if="!admin.internal">
+ <ActionButton icon="icon-close" @click="deleteAdditionalAdmin(admin)">
+ {{ $t('privacy', 'Remove external admin') }}
+ </ActionButton>
+ </Actions>
+ </div>
+
+ <div v-if="$is_admin">
+ <Actions v-if="!isAdding" class="addAdditionalAdmin">
+ <ActionButton icon="icon-add" @click.stop.prevent="openNewAdmin">
+ {{ $t('privacy', 'Add external admin') }}
+ </ActionButton>
+ </Actions>
+ <form v-if="isAdding"
+ v-click-outside="closeNewAdmin"
+ class="addAdditionalAdminFormContainer"
+ @submit.prevent="addAdditionalAdmin">
+ <input v-model="newAdditionalAdminInputField"
+ type="text"
+ maxlength="64"
+ autocomplete="new-password"
+ autocorrect="off"
+ autocapitalize="off"
+ spellcheck="false"
+ :placeholder="$t('privacy', 'Name of external admin')">
+ <input type="submit" value="" class="icon-confirm">
+ <!-- add icon-loading -->
+ </form>
+ </div>
+ </div>
+</template>
+
+<script>
+import Vue from 'vue'
+import ClickOutside from 'vue-click-outside'
+import HttpClient from '@nextcloud/axios'
+import { generateUrl } from '@nextcloud/router'
+import Actions from '@nextcloud/vue/dist/Components/Actions'
+import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
+import Avatar from '@nextcloud/vue/dist/Components/Avatar'
+
+export default {
+ name: 'Admins',
+ components: {
+ Actions,
+ ActionButton,
+ Avatar,
+ },
+ directives: {
+ ClickOutside,
+ },
+ data() {
+ return {
+ admins: [],
+ newAdditionalAdminInputField: '',
+ isLoading: true,
+ isAdding: false,
+ isSavingChanges: false,
+ }
+ },
+ /**
+ * This function is called on mount of the Vue component
+ * It sets the isAdmin property and
+ * loads additional admins from the server
+ */
+ async mounted() {
+ const url = generateUrl('/apps/privacy/api/admins')
+ try {
+ const resp = await HttpClient.get(url)
+ Vue.set(this, 'admins', resp.data)
+ } catch (error) {
+ console.error(error)
+ this.$toast('Error loading additional administrator.')
+ } finally {
+ this.isLoading = false
+ }
+ },
+ methods: {
+ /**
+ * Opens the new Admin dialog
+ */
+ openNewAdmin() {
+ this.isAdding = true
+ },
+ /**
+ * Closes the new Admin dialog and resets the input field
+ */
+ closeNewAdmin() {
+ this.isAdding = false
+ this.newAdditionalAdminInputField = ''
+ },
+ /**
+ * Creates an additional (virtual) admin on the server
+ *
+ * @returns {Promise<void>}
+ */
+ async addAdditionalAdmin() {
+ const url = generateUrl('/apps/privacy/api/admins')
+ this.isSavingChanges = true
+
+ try {
+ const response = await HttpClient.post(url, { name: this.newAdditionalAdminInputField })
+ this.admins.push(response.data)
+ } catch (error) {
+ console.error(error)
+ this.$toast('Error adding new administrator.')
+ } finally {
+ this.isSavingChanges = false
+ this.isAdding = false
+ this.newAdditionalAdminInputField = ''
+ }
+ },
+ /**
+ * Deletes an additional (virtual) admin from the server
+ *
+ * @param {Object} admin The admin object from this.admins
+ * @returns {Promise<void>}
+ */
+ async deleteAdditionalAdmin(admin) {
+ const url = generateUrl('/apps/privacy/api/admins/{id}', { id: admin.id })
+
+ try {
+ await HttpClient.delete(url)
+
+ const index = this.admins.indexOf(admin)
+ if (index !== -1) {
+ this.admins.splice(index, 1)
+ }
+ } catch (error) {
+ console.error(error)
+ this.$toast('Error deleting new administrator.')
+ }
+ },
+ },
+}
+</script>
diff --git a/src/components/Encryption.vue b/src/components/Encryption.vue
new file mode 100644
index 0000000..ec6d8f5
--- /dev/null
+++ b/src/components/Encryption.vue
@@ -0,0 +1,137 @@
+<!--
+ - @copyright Copyright (c) 2019 Georg Ehrke <oc.list@georgehrke.com>
+ - @author Georg Ehrke <oc.list@georgehrke.com>
+ -
+ - @license GNU AGPL version 3 or any later version
+ -
+ - This program is free software: you can redistribute it and/or modify
+ - it under the terms of the GNU Affero General Public License as
+ - published by the Free Software Foundation, either version 3 of the
+ - License, or (at your option) any later version.
+ -
+ - This program is distributed in the hope that it will be useful,
+ - but WITHOUT ANY WARRANTY; without even the implied warranty of
+ - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ - GNU Affero General Public License for more details.
+ -
+ - You should have received a copy of the GNU Affero General Public License
+ - along with this program. If not, see <http://www.gnu.org/licenses/>.
+ -
+ -->
+
+<template>
+ <div class="who-has-access">
+ <!-- eslint-disable-next-line vue/no-v-html -->
+ <p v-show="!isEditing" v-html="label" />
+ <Actions v-if="$is_admin && !isEditing">
+ <ActionButton icon="icon-rename" @click.stop.prevent="openEditFullDiskEncryptionForm" />
+ </Actions>
+ <div v-if="isEditing" v-click-outside="cancelEditFullDiskEncryptionForm">
+ <form>
+ <input id="fullDiskEncryptionEnabledCheckbox"
+ v-model="fullDiskEncryptionEnabled"
+ :disabled="isSavingChanges"
+ type="checkbox"
+ name="fullDiskEncryptionEnabledCheckbox"
+ class="checkbox"
+ @change="saveFullDiskEncryptionForm">
+ <label for="fullDiskEncryptionEnabledCheckbox">
+ {{ $t('privacy', 'This server is using full-disk-encryption.') }}
+ </label>
+ </form>
+ </div>
+ </div>
+</template>
+
+<script>
+import ClickOutside from 'vue-click-outside'
+
+import HttpClient from '@nextcloud/axios'
+import { generateUrl } from '@nextcloud/router'
+import Actions from '@nextcloud/vue/dist/Components/Actions'
+import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
+import { loadState } from '@nextcloud/initial-state'
+
+export default {
+ name: 'Encryption',
+ components: {
+ Actions,
+ ActionButton,
+ },
+ directives: {
+ ClickOutside,
+ },
+ data() {
+ return {
+ fullDiskEncryptionEnabled: false,
+ serverSideEncryptionEnabled: false,
+ isEditing: false,
+ isSavingChanges: false,
+ }
+ },
+ computed: {
+ label() {
+ if (!this.serverSideEncryptionEnabled && !this.fullDiskEncryptionEnabled) {
+ return this.$t('privacy', 'Your files are not protected by encryption.')
+ } else if (this.serverSideEncryptionEnabled && !this.fullDiskEncryptionEnabled) {
+ return this.$t('privacy', 'Your files are encrypted with {linkopen}server-side-encryption ↗{linkclose}.')
+ .replace('{linkopen}', '<a href="https://nextcloud.com/blog/encryption-in-nextcloud/" target="_blank" title="" rel="noreferrer noopener">')
+ .replace('{linkclose}', '</a>')
+ } else if (!this.serverSideEncryptionEnabled && this.fullDiskEncryptionEnabled) {
+ return this.$t('privacy', 'This server is protected with full-disk-encryption.')
+ } else {
+ return this.$t('privacy', 'Your files are encrypted with {linkopen}server-side-encryption ↗{linkclose}. Additionally, this server is protected with full-disk-encryption.')
+ .replace('{linkopen}', '<a href="https://nextcloud.com/blog/encryption-in-nextcloud/" target="_blank" title="" rel="noreferrer noopener">')
+ .replace('{linkclose}', '</a>')
+ }
+ },
+ },
+ /**
+ * This function is called on mount of the Vue component
+ * It checks if full-disk-encryption or server-side-encryption
+ * are enabled
+ */
+ mounted() {
+ console.debug(loadState('privacy', 'fullDiskEncryptionEnabled'))
+ console.debug(loadState('privacy', 'serverSideEncryptionEnabled'))
+ this.fullDiskEncryptionEnabled = loadState('privacy', 'fullDiskEncryptionEnabled') === 1
+ this.serverSideEncryptionEnabled = loadState('privacy', 'serverSideEncryptionEnabled') === 1
+ },
+ methods: {
+ /**
+ * Opens the form to edit the full-disk-encryption-state
+ */
+ openEditFullDiskEncryptionForm() {
+ this.isEditing = true
+ },
+ /**
+ * Closes the form to edit the full-disk-encryption-state
+ */
+ cancelEditFullDiskEncryptionForm() {
+ this.isEditing = false
+ },
+ /**
+ * Saves the new full-disk-encryption-state
+ *
+ * @returns {Promise<void>}
+ */
+ async saveFullDiskEncryptionForm() {
+ const url = generateUrl('/apps/privacy/api/fullDiskEncryption')
+ this.isSavingChanges = true
+
+ try {
+ await HttpClient.post(url, { enabled: this.fullDiskEncryptionEnabled ? '1' : '0' })
+ } catch (error) {
+ console.error(error)
+ this.$toast('Error saving new state of full-disk-encryption')
+
+ // Reset state
+ this.fullDiskEncryptionEnabled = !this.fullDiskEncryptionEnabled
+ } finally {
+ this.isSavingChanges = false
+ this.isEditing = false
+ }
+ },
+ },
+}
+</script>
diff --git a/src/components/Location.vue b/src/components/Location.vue
new file mode 100644
index 0000000..17fb131
--- /dev/null
+++ b/src/components/Location.vue
@@ -0,0 +1,147 @@
+<!--
+ - @copyright Copyright (c) 2019 Georg Ehrke <oc.list@georgehrke.com>
+ - @author Georg Ehrke <oc.list@georgehrke.com>
+ -
+ - @license GNU AGPL version 3 or any later version
+ -
+ - This program is free software: you can redistribute it and/or modify
+ - it under the terms of the GNU Affero General Public License as
+ - published by the Free Software Foundation, either version 3 of the
+ - License, or (at your option) any later version.
+ -
+ - This program is distributed in the hope that it will be useful,
+ - but WITHOUT ANY WARRANTY; without even the implied warranty of
+ - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ - GNU Affero General Public License for more details.
+ -
+ - You should have received a copy of the GNU Affero General Public License
+ - along with this program. If not, see <http://www.gnu.org/licenses/>.
+ -
+ -->
+
+<template>
+ <div class="where-is-my-data">
+ <p v-show="!isEditingLocation">
+ <span v-show="country">{{ $t('privacy', 'Your data is located in: ') }}<strong>{{ country }}.</strong></span>
+ <span v-show="!country">{{ $t('privacy', 'The admin hasn\'t selected the location of the server yet.') }}</span>
+ <Actions v-if="$is_admin">
+ <ActionButton icon="icon-rename" @click="editLocation">
+ {{ t('privacy', 'Change data location') }}
+ </ActionButton>
+ </Actions>
+ </p>
+ <div v-show="isEditingLocation" class="multiselect-container">
+ <Multiselect
+ :disabled="isSavingChanges"
+ :options="options"
+ :searchable="true"
+ track-by="code"
+ label="label"
+ :placeholder="$t('privacy', 'Please select a country')"
+ @input="onChange" />
+ <span v-show="isSavingChanges" class="icon icon-loading" />
+ </div>
+ <Map />
+ </div>
+</template>
+
+<script>
+import HttpClient from '@nextcloud/axios'
+import { generateUrl } from '@nextcloud/router'
+import Multiselect from '@nextcloud/vue/dist/Components/Multiselect'
+import Actions from '@nextcloud/vue/dist/Components/Actions'
+import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
+import { loadState } from '@nextcloud/initial-state'
+
+import {
+ getCountryList,
+ getNameForCountryCode,
+} from '../nameProvider.js'
+import Map from './Map.vue'
+
+export default {
+ name: 'Location',
+ components: {
+ ActionButton,
+ Actions,
+ Map,
+ Multiselect,
+ },
+ data() {
+ return {
+ selectedCountry: 'de',
+ isEditingLocation: false,
+ isSavingChanges: false,
+ }
+ },
+ computed: {
+ country() {
+ return getNameForCountryCode(this.$data.selectedCountry)
+ },
+ options() {
+ return getCountryList()
+ },
+ },
+ watch: {
+ selectedCountry: (newCountry, oldCountry) => {
+ if (oldCountry !== '') {
+ const oldElm = document.querySelector('.where-is-my-data #' + oldCountry)
+
+ if (oldElm) {
+ oldElm.style.fill = null
+ }
+ }
+
+ if (newCountry !== '') {
+ const newElm = document.querySelector('.where-is-my-data #' + newCountry)
+
+ if (newElm) {
+ newElm.style.fill = 'var(--color-primary)'
+ }
+ }
+ },
+ },
+ /**
+ * This function is called on mount of the Vue component
+ * It loads the location of the server.
+ */
+ mounted() {
+ this.selectedCountry = loadState('privacy', 'location')
+ if (this.selectedCountry !== '') {
+ const elm = document.querySelector('.where-is-my-data #' + this.selectedCountry)
+ if (elm) {
+ elm.style.fill = '#e6605c'
+ }
+ }
+ },
+ methods: {
+ /**
+ * Opens the edit location form
+ */
+ editLocation() {
+ this.isEditingLocation = true
+ },
+ /**
+ * Saves changes to the location form
+ *
+ * @param {Object} value The new country object
+ * @returns {Promise<void>}
+ */
+ async onChange(value) {
+ const url = generateUrl('/apps/privacy/api/location')
+ this.isSavingChanges = true
+
+ try {
+ await HttpClient.post(url, { code: value.code })
+ this.selectedCountry = value.code
+ } catch (error) {
+ console.error(error)
+ this.$toast('Error saving new location of the server')
+ } finally {
+ this.isEditingLocation = false
+ this.isSavingChanges = false
+ }
+ },
+ },
+}
+</script>
diff --git a/src/Map.vue b/src/components/Map.vue
index f70b380..f70b380 100644
--- a/src/Map.vue
+++ b/src/components/Map.vue
diff --git a/src/components/Shares.vue b/src/components/Shares.vue
new file mode 100644
index 0000000..b0079c6
--- /dev/null
+++ b/src/components/Shares.vue
@@ -0,0 +1,101 @@
+<!--
+ - @copyright Copyright (c) 2019 Georg Ehrke <oc.list@georgehrke.com>
+ - @author Georg Ehrke <oc.list@georgehrke.com>
+ -
+ - @license GNU AGPL version 3 or any later version
+ -
+ - This program is free software: you can redistribute it and/or modify
+ - it under the terms of the GNU Affero General Public License as
+ - published by the Free Software Foundation, either version 3 of the
+ - License, or (at your option) any later version.
+ -
+ - This program is distributed in the hope that it will be useful,
+ - but WITHOUT ANY WARRANTY; without even the implied warranty of
+ - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ - GNU Affero General Public License for more details.
+ -
+ - You should have received a copy of the GNU Affero General Public License
+ - along with this program. If not, see <http://www.gnu.org/licenses/>.
+ -
+ -->
+
+<template>
+ <div class="who-has-access">
+ <span :class="{hidden: !isLoading}" class="icon icon-loading" />
+ <span :class="{hidden: !isEmptyList}">
+ {{ $t('privacy', 'You don\'t have any shares with individual users.') }}
+ </span>
+ <Avatar v-for="uid in uniqueShareUIDs"
+ :key="uid"
+ :user="uid"
+ :display-name="uidDisplaynameMap[uid]"
+ :size="64" />
+ </div>
+</template>
+
+<script>
+import Vue from 'vue'
+
+import HttpClient from '@nextcloud/axios'
+import { generateOcsUrl } from '@nextcloud/router'
+import { getCurrentUser } from '@nextcloud/auth'
+import Avatar from '@nextcloud/vue/dist/Components/Avatar'
+
+export default {
+ name: 'Shares',
+ components: {
+ Avatar,
+ },
+ data() {
+ return {
+ uniqueShareUIDs: [],
+ uidDisplaynameMap: {},
+ isLoading: true,
+ }
+ },
+ computed: {
+ /**
+ * Checks if the list of shares is empty
+ *
+ * @returns {boolean}
+ */
+ isEmptyList() {
+ return this.isLoading === false && this.uniqueShareUIDs.length === 0
+ },
+ },
+ /**
+ * This function is called on mount of the Vue component
+ * It loads the people shared with
+ */
+ async mounted() {
+ const url = generateOcsUrl('/apps/files_sharing/api/v1/shares?format=json&shared_with_me=false')
+ const currentUserId = getCurrentUser().uid
+
+ try {
+ const resp = await HttpClient.get(url)
+ resp.data.ocs.data.forEach((d) => {
+ if (d.share_with === currentUserId) {
+ return
+ }
+
+ switch (d.share_type) {
+ case 0:
+ if (this.uniqueShareUIDs.indexOf(d.share_with) === -1) {
+ this.uniqueShareUIDs.push(d.share_with)
+ Vue.set(this.uidDisplaynameMap, d.share_with, d.share_with_displayname)
+ }
+ break
+
+ default:
+ break
+ }
+ })
+ } catch (error) {
+ console.error(error)
+ this.$toast('Error loading information about shares.')
+ } finally {
+ this.isLoading = false
+ }
+ },
+}
+</script>
diff --git a/src/main.js b/src/main.js
index a1c4b1a..3602517 100644
--- a/src/main.js
+++ b/src/main.js
@@ -18,13 +18,15 @@
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
-import '@babel/polyfill'
+import 'core-js/stable'
import Vue from 'vue'
+import { linkTo } from '@nextcloud/router'
+import { translate, translatePlural } from '@nextcloud/l10n'
-const Admins = () => import('./Admins.vue')
-const Location = () => import('./Location.vue')
-const Encryption = () => import('./Encryption.vue')
-const Shares = () => import('./Shares.vue')
+const Admins = () => import('./components/Admins.vue')
+const Location = () => import('./components/Location.vue')
+const Encryption = () => import('./components/Encryption.vue')
+const Shares = () => import('./components/Shares.vue')
// CSP config for webpack dynamic chunk loading
// eslint-disable-next-line
@@ -35,36 +37,32 @@ __webpack_nonce__ = btoa(OC.requestToken)
// OC.generateUrl ensure the index.php (or not)
// We do not want the index.php since we're loading files
// eslint-disable-next-line
-__webpack_public_path__ = OC.linkTo('privacy', 'js/')
+__webpack_public_path__ = linkTo('privacy', 'js/')
-Vue.prototype.t = t
-Vue.prototype.OC = OC
+Vue.prototype.$t = translate
+Vue.prototype.$n = translatePlural
+Vue.prototype.$is_admin = OC.isUserAdmin()
+Vue.prototype.$toast = OCP.Toast // eslint-disable-line no-undef
+
+// The nextcloud-vue package does currently rely on t and n
+Vue.prototype.t = translate
+Vue.prototype.n = translatePlural
const location = new Vue({
el: '#privacy_where_location',
- render: h => h(Location)
+ render: h => h(Location),
})
const admins = new Vue({
el: '#privacy_access_admins',
- render: h => h(Admins)
+ render: h => h(Admins),
})
const shares = new Vue({
el: '#privacy_access_shares',
- render: h => h(Shares)
+ render: h => h(Shares),
})
-
-const privacyAccesEncryption = document.getElementById('privacy_access_encryption')
-const fullDiskEncryptionEnabled = privacyAccesEncryption.dataset.fullDiskEncryption === '1'
-const serverSideEncryptionEnabled = privacyAccesEncryption.dataset.serverSideEncryption === '1'
const encryption = new Vue({
el: '#privacy_access_encryption',
- data() {
- return {
- fullDiskEncryptionEnabled,
- serverSideEncryptionEnabled
- }
- },
- render: h => h(Encryption)
+ render: h => h(Encryption),
})
export default { location, admins, shares, encryption }
diff --git a/src/nameProvider.js b/src/nameProvider.js
index e811207..8a2ee46 100644
--- a/src/nameProvider.js
+++ b/src/nameProvider.js
@@ -172,7 +172,7 @@ const list = [
{ code: 'ye', label: t('privacy', 'Yemen') },
{ code: 'za', label: t('privacy', 'South Africa') },
{ code: 'zm', label: t('privacy', 'Zambia') },
- { code: 'zw', label: t('privacy', 'Zimbabwe') }
+ { code: 'zw', label: t('privacy', 'Zimbabwe') },
]
export function getCountryList() {