aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files_sharing/src/utils
diff options
context:
space:
mode:
Diffstat (limited to 'apps/files_sharing/src/utils')
-rw-r--r--apps/files_sharing/src/utils/AccountIcon.spec.ts40
-rw-r--r--apps/files_sharing/src/utils/AccountIcon.ts28
-rw-r--r--apps/files_sharing/src/utils/GeneratePassword.ts66
-rw-r--r--apps/files_sharing/src/utils/NodeShareUtils.ts58
-rw-r--r--apps/files_sharing/src/utils/SharedWithMe.js65
5 files changed, 257 insertions, 0 deletions
diff --git a/apps/files_sharing/src/utils/AccountIcon.spec.ts b/apps/files_sharing/src/utils/AccountIcon.spec.ts
new file mode 100644
index 00000000000..bbc7f031774
--- /dev/null
+++ b/apps/files_sharing/src/utils/AccountIcon.spec.ts
@@ -0,0 +1,40 @@
+/*!
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+import { describe, expect, it, afterEach } from 'vitest'
+import { generateAvatarSvg } from './AccountIcon'
+describe('AccountIcon', () => {
+
+ afterEach(() => {
+ delete document.body.dataset.themes
+ })
+
+ it('should generate regular account avatar svg', () => {
+ const svg = generateAvatarSvg('admin')
+ expect(svg).toContain('/avatar/admin/32')
+ expect(svg).not.toContain('dark')
+ expect(svg).toContain('?guestFallback=true')
+ })
+
+ it('should generate guest account avatar svg', () => {
+ const svg = generateAvatarSvg('admin', true)
+ expect(svg).toContain('/avatar/guest/admin/32')
+ expect(svg).not.toContain('dark')
+ expect(svg).not.toContain('?guestFallback=true')
+ })
+
+ it('should generate dark mode account avatar svg', () => {
+ document.body.dataset.themes = 'dark'
+ const svg = generateAvatarSvg('admin')
+ expect(svg).toContain('/avatar/admin/32/dark')
+ expect(svg).toContain('?guestFallback=true')
+ })
+
+ it('should generate dark mode guest account avatar svg', () => {
+ document.body.dataset.themes = 'dark'
+ const svg = generateAvatarSvg('admin', true)
+ expect(svg).toContain('/avatar/guest/admin/32/dark')
+ expect(svg).not.toContain('?guestFallback=true')
+ })
+})
diff --git a/apps/files_sharing/src/utils/AccountIcon.ts b/apps/files_sharing/src/utils/AccountIcon.ts
new file mode 100644
index 00000000000..21732f08f68
--- /dev/null
+++ b/apps/files_sharing/src/utils/AccountIcon.ts
@@ -0,0 +1,28 @@
+/*!
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+import { generateUrl } from '@nextcloud/router'
+
+const isDarkMode = () => {
+ return window?.matchMedia?.('(prefers-color-scheme: dark)')?.matches === true
+ || document.querySelector('[data-themes*=dark]') !== null
+}
+
+export const generateAvatarSvg = (userId: string, isGuest = false) => {
+ // normal avatar url: /avatar/{userId}/32?guestFallback=true
+ // dark avatar url: /avatar/{userId}/32/dark?guestFallback=true
+ // guest avatar url: /avatar/guest/{userId}/32
+ // guest dark avatar url: /avatar/guest/{userId}/32/dark
+ const basePath = isGuest ? `/avatar/guest/${userId}` : `/avatar/${userId}`
+ const darkModePath = isDarkMode() ? '/dark' : ''
+ const guestFallback = isGuest ? '' : '?guestFallback=true'
+
+ const url = `${basePath}/32${darkModePath}${guestFallback}`
+ const avatarUrl = generateUrl(url, { userId })
+
+ return `<svg width="32" height="32" viewBox="0 0 32 32"
+ xmlns="http://www.w3.org/2000/svg" class="sharing-status__avatar">
+ <image href="${avatarUrl}" height="32" width="32" />
+ </svg>`
+}
diff --git a/apps/files_sharing/src/utils/GeneratePassword.ts b/apps/files_sharing/src/utils/GeneratePassword.ts
new file mode 100644
index 00000000000..82efaaa69d4
--- /dev/null
+++ b/apps/files_sharing/src/utils/GeneratePassword.ts
@@ -0,0 +1,66 @@
+/**
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import axios from '@nextcloud/axios'
+import Config from '../services/ConfigService.ts'
+import { showError, showSuccess } from '@nextcloud/dialogs'
+import { translate as t } from '@nextcloud/l10n'
+
+const config = new Config()
+// note: some chars removed on purpose to make them human friendly when read out
+const passwordSet = 'abcdefgijkmnopqrstwxyzABCDEFGHJKLMNPQRSTWXYZ23456789'
+
+/**
+ * Generate a valid policy password or request a valid password if password_policy is enabled
+ *
+ * @param {boolean} verbose If enabled the the status is shown to the user via toast
+ */
+export default async function(verbose = false): Promise<string> {
+ // password policy is enabled, let's request a pass
+ if (config.passwordPolicy.api && config.passwordPolicy.api.generate) {
+ try {
+ const request = await axios.get(config.passwordPolicy.api.generate)
+ if (request.data.ocs.data.password) {
+ if (verbose) {
+ showSuccess(t('files_sharing', 'Password created successfully'))
+ }
+ return request.data.ocs.data.password
+ }
+ } catch (error) {
+ console.info('Error generating password from password_policy', error)
+ if (verbose) {
+ showError(t('files_sharing', 'Error generating password from password policy'))
+ }
+ }
+ }
+
+ const array = new Uint8Array(10)
+ const ratio = passwordSet.length / 255
+ getRandomValues(array)
+ let password = ''
+ for (let i = 0; i < array.length; i++) {
+ password += passwordSet.charAt(array[i] * ratio)
+ }
+ return password
+}
+
+/**
+ * Fills the given array with cryptographically secure random values.
+ * If the crypto API is not available, it falls back to less secure Math.random().
+ * Crypto API is available in modern browsers on secure contexts (HTTPS).
+ *
+ * @param {Uint8Array} array - The array to fill with random values.
+ */
+function getRandomValues(array: Uint8Array): void {
+ if (self?.crypto?.getRandomValues) {
+ self.crypto.getRandomValues(array)
+ return
+ }
+
+ let len = array.length
+ while (len--) {
+ array[len] = Math.floor(Math.random() * 256)
+ }
+}
diff --git a/apps/files_sharing/src/utils/NodeShareUtils.ts b/apps/files_sharing/src/utils/NodeShareUtils.ts
new file mode 100644
index 00000000000..f14f981e2ad
--- /dev/null
+++ b/apps/files_sharing/src/utils/NodeShareUtils.ts
@@ -0,0 +1,58 @@
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import { getCurrentUser } from '@nextcloud/auth'
+import type { Node } from '@nextcloud/files'
+import { ShareType } from '@nextcloud/sharing'
+
+type Share = {
+ /** The recipient display name */
+ 'display-name': string
+ /** The recipient user id */
+ id: string
+ /** The share type */
+ type: ShareType
+}
+
+const getSharesAttribute = function(node: Node) {
+ return Object.values(node.attributes.sharees).flat() as Share[]
+}
+
+export const isNodeSharedWithMe = function(node: Node) {
+ const uid = getCurrentUser()?.uid
+ const shares = getSharesAttribute(node)
+
+ // If you're the owner, you can't share with yourself
+ if (node.owner === uid) {
+ return false
+ }
+
+ return shares.length > 0 && (
+ // If some shares are shared with you as a direct user share
+ shares.some(share => share.id === uid && share.type === ShareType.User)
+ // Or of the file is shared with a group you're in
+ // (if it's returned by the backend, we assume you're in it)
+ || shares.some(share => share.type === ShareType.Group)
+ )
+}
+
+export const isNodeSharedWithOthers = function(node: Node) {
+ const uid = getCurrentUser()?.uid
+ const shares = getSharesAttribute(node)
+
+ // If you're NOT the owner, you can't share with yourself
+ if (node.owner === uid) {
+ return false
+ }
+
+ return shares.length > 0
+ // If some shares are shared with you as a direct user share
+ && shares.some(share => share.id !== uid && share.type !== ShareType.Group)
+}
+
+export const isNodeShared = function(node: Node) {
+ const shares = getSharesAttribute(node)
+ return shares.length > 0
+}
diff --git a/apps/files_sharing/src/utils/SharedWithMe.js b/apps/files_sharing/src/utils/SharedWithMe.js
new file mode 100644
index 00000000000..2f63932bfbe
--- /dev/null
+++ b/apps/files_sharing/src/utils/SharedWithMe.js
@@ -0,0 +1,65 @@
+/**
+ * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import { ShareType } from '@nextcloud/sharing'
+
+const shareWithTitle = function(share) {
+ if (share.type === ShareType.Group) {
+ return t(
+ 'files_sharing',
+ 'Shared with you and the group {group} by {owner}',
+ {
+ group: share.shareWithDisplayName,
+ owner: share.ownerDisplayName,
+ },
+ undefined,
+ { escape: false },
+ )
+ } else if (share.type === ShareType.Team) {
+ return t(
+ 'files_sharing',
+ 'Shared with you and {circle} by {owner}',
+ {
+ circle: share.shareWithDisplayName,
+ owner: share.ownerDisplayName,
+ },
+ undefined,
+ { escape: false },
+ )
+ } else if (share.type === ShareType.Room) {
+ if (share.shareWithDisplayName) {
+ return t(
+ 'files_sharing',
+ 'Shared with you and the conversation {conversation} by {owner}',
+ {
+ conversation: share.shareWithDisplayName,
+ owner: share.ownerDisplayName,
+ },
+ undefined,
+ { escape: false },
+ )
+ } else {
+ return t(
+ 'files_sharing',
+ 'Shared with you in a conversation by {owner}',
+ {
+ owner: share.ownerDisplayName,
+ },
+ undefined,
+ { escape: false },
+ )
+ }
+ } else {
+ return t(
+ 'files_sharing',
+ 'Shared with you by {owner}',
+ { owner: share.ownerDisplayName },
+ undefined,
+ { escape: false },
+ )
+ }
+}
+
+export { shareWithTitle }