Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions l10n/messages.pot
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,21 @@ msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"

msgid "\"{char}\" is not allowed inside a name."
msgstr ""

msgid "\"{extension}\" is not an allowed name."
msgstr ""

msgid "\"{name}\" is an invalid folder name."
msgstr ""

msgid "\"{name}\" is not an allowed folder name"
msgstr ""

msgid "\"{segment}\" is a reserved name and not allowed."
msgstr ""

msgid "\"/\" is not allowed inside a folder name."
msgstr ""

Expand Down Expand Up @@ -76,6 +85,9 @@ msgstr ""
msgid "Home"
msgstr ""

msgid "Invalid name."
msgstr ""

msgid "Modified"
msgstr ""

Expand All @@ -88,6 +100,15 @@ msgstr ""
msgid "Name"
msgstr ""

msgid "Names must not be empty."
msgstr ""

msgid "Names must not end with \"{extension}\"."
msgstr ""

msgid "Names must not start with a dot."
msgstr ""

msgid "New"
msgstr ""

Expand Down
24 changes: 23 additions & 1 deletion lib/components/PublicAuthPrompt.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
<script setup lang="ts">
import { setGuestNickname } from '@nextcloud/auth'
import { getBuilder } from '@nextcloud/browser-storage'
import { showError } from '@nextcloud/dialogs'
import { computed, ref, useTemplateRef, watch } from 'vue'
import NcDialog from '@nextcloud/vue/components/NcDialog'
import NcNoteCard from '@nextcloud/vue/components/NcNoteCard'
import NcTextField from '@nextcloud/vue/components/NcTextField'
import { showError } from '../toast.ts'
import { getGuestNameValidity } from '../utils/guestNameValidity.ts'
import { t } from '../utils/l10n.ts'
import { logger } from '../utils/logger.ts'

Expand Down Expand Up @@ -70,6 +71,18 @@ watch(() => props.nickname, () => {
name.value = props.nickname
})

watch(name, (newName) => {
// Check validity of the new name
const validity = getGuestNameValidity(newName)
if (!validity) {
// If the nickname is not valid, show an error
inputElement.value.setCustomValidity(validity)
inputElement.value.reportValidity()
inputElement.value.focus()
return
}
})

const buttons = computed(() => {
const cancelButton = {
label: t('Cancel'),
Expand Down Expand Up @@ -97,6 +110,15 @@ const buttons = computed(() => {
function onSubmit() {
const nickname = name.value.trim()

const validity = getGuestNameValidity(nickname)
if (validity) {
// If the nickname is not valid, show an error
inputElement.value.setCustomValidity(validity)
inputElement.value.reportValidity()
inputElement.value.focus()
return
}

if (nickname === '') {
// Show error if the nickname is empty
inputElement.value.setCustomValidity(t('You cannot leave the name empty.'))
Expand Down
1 change: 0 additions & 1 deletion lib/public-auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,5 @@ export async function showGuestUserPrompt(props: GuestUserPromptOptions): Promis
defineAsyncComponent(() => import('./components/PublicAuthPrompt.vue')),
props,
)
/// @ts-expect-error TODO: remove when fixed upstream: https://github.com/nextcloud-libraries/nextcloud-vue/issues/6902
return name
}
45 changes: 45 additions & 0 deletions lib/utils/guestNameValidity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*!
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { InvalidFilenameError, InvalidFilenameErrorReason, validateFilename } from '@nextcloud/files'
import { t } from '../utils/l10n.ts'

/**
* Get the validity of a filename (empty if valid).
* This can be used for `setCustomValidity` on input elements
*
* @param name The filename
*/
export function getGuestNameValidity(name: string): string {
if (name.trim() === '') {
return t('Names must not be empty.')
}

if (name.startsWith('.')) {
return t('Names must not start with a dot.')
}

try {
validateFilename(name)
return ''
} catch (error) {
if (!(error instanceof InvalidFilenameError)) {
throw error
}

switch (error.reason) {
case InvalidFilenameErrorReason.Character:
return t('"{char}" is not allowed inside a name.', { char: error.segment })
case InvalidFilenameErrorReason.ReservedName:
return t('"{segment}" is a reserved name and not allowed.', { segment: error.segment })
case InvalidFilenameErrorReason.Extension:
if (error.segment.match(/\.[a-z]/i)) {
return t('"{extension}" is not an allowed name.', { extension: error.segment })
}
return t('Names must not end with "{extension}".', { extension: error.segment })
default:
return t('Invalid name.')
}
}
}
Loading