summaryrefslogtreecommitdiffstats
path: root/src/components/CircleDetails/CirclePasswordSettings.vue
diff options
context:
space:
mode:
authorRichard Steinmetz <richard@steinmetz.cloud>2022-06-03 18:23:19 +0200
committerRichard Steinmetz <richard@steinmetz.cloud>2022-06-28 10:16:41 +0200
commit51ed90ee20269eb46288b187e6250e23cb25b94f (patch)
tree0d8888341976f037918cc64fb0ac8b0576aa097b /src/components/CircleDetails/CirclePasswordSettings.vue
parent2e155bb9c0252dd26764fc6dc9f30d7088959707 (diff)
Implement share password settings
Signed-off-by: Richard Steinmetz <richard@steinmetz.cloud>
Diffstat (limited to 'src/components/CircleDetails/CirclePasswordSettings.vue')
-rw-r--r--src/components/CircleDetails/CirclePasswordSettings.vue274
1 files changed, 274 insertions, 0 deletions
diff --git a/src/components/CircleDetails/CirclePasswordSettings.vue b/src/components/CircleDetails/CirclePasswordSettings.vue
new file mode 100644
index 00000000..c1795393
--- /dev/null
+++ b/src/components/CircleDetails/CirclePasswordSettings.vue
@@ -0,0 +1,274 @@
+<!--
+ - @copyright Copyright (c) 2022 Richard Steinmetz <richard@steinmetz.cloud>
+ -
+ - @author Richard Steinmetz <richard@steinmetz.cloud>
+ -
+ - @license AGPL-3.0-or-later
+ -
+ - 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>
+ <ul>
+ <li class="circle-config">
+ <ContentHeading class="circle-config__title">
+ {{ t('contacts', 'Password protection') }}
+ </ContentHeading>
+
+ <ul class="circle-config__list">
+ <CheckboxRadioSwitch :checked="enforcePasswordProtection"
+ :loading="loading.includes(ENFORCE_PASSWORD_PROTECTION)"
+ :disabled="loading.length > 0"
+ wrapper-element="li"
+ @update:checked="changePasswordProtection">
+ {{ t('contacts', 'Enforce Password protection on Files shared to this circle') }}
+ </CheckboxRadioSwitch>
+
+ <CheckboxRadioSwitch v-if="enforcePasswordProtection"
+ :checked="useUniquePassword || showUniquePasswordInput"
+ :loading="loading.includes(USE_UNIQUE_PASSWORD)"
+ :disabled="loading.length > 0"
+ wrapper-element="li"
+ @update:checked="changeUseUniquePassword">
+ {{ t('contacts', 'Use a unique password for all shares to this circles') }}
+ </CheckboxRadioSwitch>
+
+ <li class="unique-password">
+ <template v-if="showUniquePasswordInput">
+ <input
+ v-model="uniquePassword"
+ :disabled="loading.length > 0"
+ :placeholder="t('contacts', 'Unique password ...')"
+ type="text"
+ @keyup.enter="saveUniquePassword" />
+ <Button
+ type="tertiary-no-background"
+ :disabled="loading.length > 0 || uniquePassword.length === 0"
+ @click="saveUniquePassword">
+ {{ t('contacts', 'Save') }}
+ </Button>
+ </template>
+ <Button
+ v-else-if="useUniquePassword"
+ class="change-unique-password"
+ @click="onClickChangePassword">
+ {{ t('contacts', 'Change unique password') }}
+ </Button>
+
+ <div v-if="uniquePasswordError" class="unique-password-error">
+ {{ t('contacts', 'Failed to save password. Please try again later.') }}
+ </div>
+ </li>
+ </ul>
+ </li>
+ </ul>
+</template>
+
+<script>
+import ContentHeading from './ContentHeading'
+import CheckboxRadioSwitch from '@nextcloud/vue/dist/Components/CheckboxRadioSwitch'
+import Button from '@nextcloud/vue/dist/Components/Button'
+
+// Circle setting keys
+const ENFORCE_PASSWORD_PROTECTION = 'enforce_password'
+const USE_UNIQUE_PASSWORD = 'password_single_enabled'
+const UNIQUE_PASSWORD = 'password_single'
+
+export default {
+ name: 'CirclePasswordSettings',
+ components: {
+ ContentHeading,
+ CheckboxRadioSwitch,
+ Button,
+ },
+ props: {
+ circle: {
+ type: Object,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ ENFORCE_PASSWORD_PROTECTION,
+ USE_UNIQUE_PASSWORD,
+ UNIQUE_PASSWORD,
+
+ loading: [],
+
+ uniquePassword: '',
+ uniquePasswordError: false,
+ showUniquePasswordInput: false,
+ }
+ },
+ computed: {
+ /**
+ * @return {string}
+ */
+ circleId() {
+ return this.circle._data.id
+ },
+
+ /**
+ * @return {boolean}
+ */
+ enforcePasswordProtection() {
+ const value = this.circle._data.settings[ENFORCE_PASSWORD_PROTECTION]
+ return value === '1' || value === 'true'
+ },
+
+ /**
+ * @return {boolean}
+ */
+ useUniquePassword() {
+ const value = this.circle._data.settings[USE_UNIQUE_PASSWORD]
+ return value === '1' || value === 'true'
+ },
+ },
+ methods: {
+ /**
+ * Change handler for enforcePasswordProtection checkbox.
+ */
+ async changePasswordProtection() {
+ this.loading.push(ENFORCE_PASSWORD_PROTECTION)
+ try {
+ const newValue = !this.enforcePasswordProtection
+
+ // Also disable unique password setting
+ if (!newValue && this.useUniquePassword) {
+ await this.saveUseUniquePassword(false)
+ }
+
+ // Also hide password input
+ if (!newValue && this.showUniquePasswordInput) {
+ this.showUniquePasswordInput = false
+ }
+
+ await this.$store.dispatch('editCircleSetting', {
+ circleId: this.circleId,
+ setting: {
+ setting: ENFORCE_PASSWORD_PROTECTION,
+ value: newValue.toString(),
+ },
+ })
+ } finally {
+ this.loading = this.loading.filter(item => item !== ENFORCE_PASSWORD_PROTECTION)
+ }
+ },
+
+ /**
+ * Change handler for useUniquePassword checkbox.
+ */
+ async changeUseUniquePassword() {
+ // Only update backend if the user disables the setting.
+ // It will be enabled once a unique password has been set.
+ if (!this.useUniquePassword) {
+ this.showUniquePasswordInput = !this.showUniquePasswordInput
+ return
+ }
+
+ await this.saveUseUniquePassword(!this.useUniquePassword)
+ },
+
+ /**
+ * Update backend with given value for useUniquePassword.
+ *
+ * @param {boolean} value New value
+ */
+ async saveUseUniquePassword(value) {
+ this.loading.push(USE_UNIQUE_PASSWORD)
+ try {
+ await this.$store.dispatch('editCircleSetting', {
+ circleId: this.circleId,
+ setting: {
+ setting: USE_UNIQUE_PASSWORD,
+ value: value.toString(),
+ },
+ })
+
+ // Reset unique password input state if disabled
+ if (!value) {
+ this.uniquePassword = ''
+ this.showUniquePasswordInput = false
+ }
+ } finally {
+ this.loading = this.loading.filter(item => item !== USE_UNIQUE_PASSWORD)
+ }
+ },
+
+ /**
+ * Persist uniquePassword to backend.
+ */
+ async saveUniquePassword() {
+ if (this.uniquePassword.length === 0) {
+ return
+ }
+
+ this.loading.push(UNIQUE_PASSWORD)
+ this.uniquePasswordError = false
+ try {
+ if (!this.useUniquePassword) {
+ await this.saveUseUniquePassword(true)
+ }
+
+ await this.$store.dispatch('editCircleSetting', {
+ circleId: this.circleId,
+ setting: {
+ setting: UNIQUE_PASSWORD,
+ value: this.uniquePassword,
+ },
+ })
+
+ // Show change button after saving the password
+ this.showUniquePasswordInput = false
+ this.uniquePassword = ''
+ } catch {
+ this.uniquePasswordError = true
+ } finally {
+ this.loading = this.loading.filter(item => item !== UNIQUE_PASSWORD)
+ }
+ },
+
+ /**
+ * Click handler for the button to show the uniquePassword input.
+ */
+ onClickChangePassword() {
+ this.showUniquePasswordInput = true
+ },
+ },
+}
+</script>
+
+<style lang="scss" scoped>
+.unique-password {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ width: 100%;
+
+ input {
+ flex: 1 auto;
+ max-width: 200px;
+ }
+
+ .change-unique-password {
+ margin-top: 5px;
+ }
+
+ // Force wrap error into a new line
+ .unique-password-error {
+ flex: 1 100%;
+ }
+}
+</style>