Skip to content

Commit

Permalink
Merge pull request #46892 from nextcloud/fix/add-note-to-recipient
Browse files Browse the repository at this point in the history
fix(files_sharing): Add missing "note to recipient"
  • Loading branch information
susnux authored Jul 31, 2024
2 parents 6a0edef + 1ce365a commit 4ecd592
Show file tree
Hide file tree
Showing 99 changed files with 598 additions and 144 deletions.
38 changes: 38 additions & 0 deletions apps/files_sharing/src/files_headers/noteToRecipient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import type { ComponentPublicInstance, VueConstructor } from 'vue'

import { Folder, Header, registerFileListHeaders } from '@nextcloud/files'
import Vue from 'vue'

type IFilesHeaderNoteToRecipient = ComponentPublicInstance & { updateFolder: (folder: Folder) => void }

/**
* Register the "note to recipient" as a files list header
*/
export default function registerNoteToRecipient() {
let FilesHeaderNoteToRecipient: VueConstructor
let instance: IFilesHeaderNoteToRecipient

registerFileListHeaders(new Header({
id: 'note-to-recipient',
order: 0,
// Always if there is a note
enabled: (folder: Folder) => Boolean(folder.attributes.note),
// Update the root folder if needed
updated: (folder: Folder) => {
instance.updateFolder(folder)
},
// render simply spawns the component
render: async (el: HTMLElement, folder: Folder) => {
if (FilesHeaderNoteToRecipient === undefined) {
const { default: component } = await import('../views/FilesHeaderNoteToRecipient.vue')
FilesHeaderNoteToRecipient = Vue.extend(component)
}
instance = new FilesHeaderNoteToRecipient().$mount(el) as unknown as IFilesHeaderNoteToRecipient
instance.updateFolder(folder)
},
}))
}
10 changes: 8 additions & 2 deletions apps/files_sharing/src/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,29 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { addNewFileMenuEntry, registerDavProperty } from '@nextcloud/files'
import { registerAccountFilter } from './filters/AccountFilter'
import { entry as newFileRequest } from './new/newFileRequest.ts'

import registerNoteToRecipient from './files_headers/noteToRecipient'
import registerSharingViews from './views/shares'

import { entry as newFileRequest } from './new/newFileRequest'
import './actions/acceptShareAction'
import './actions/openInFilesAction'
import './actions/rejectShareAction'
import './actions/restoreShareAction'
import './actions/sharingStatusAction'
import { registerAccountFilter } from './filters/AccountFilter'

registerSharingViews()

addNewFileMenuEntry(newFileRequest)

registerDavProperty('nc:note', { nc: 'http://nextcloud.org/ns' })
registerDavProperty('nc:sharees', { nc: 'http://nextcloud.org/ns' })
registerDavProperty('nc:share-attributes', { nc: 'http://nextcloud.org/ns' })
registerDavProperty('oc:share-types', { oc: 'http://owncloud.org/ns' })
registerDavProperty('ocs:share-permissions', { ocs: 'http://open-collaboration-services.org/ns' })

registerAccountFilter()

// Add "note to recipient" message
registerNoteToRecipient()
73 changes: 73 additions & 0 deletions apps/files_sharing/src/views/FilesHeaderNoteToRecipient.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<!--
- SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->
<template>
<NcNoteCard v-if="note.length > 0"
class="note-to-recipient"
type="info">
<p v-if="user" class="note-to-recipient__heading">
{{ t('files_sharing', 'Note from') }}
<NcUserBubble :user="user.id" :display-name="user.displayName" />
</p>
<p v-else class="note-to-recipient__heading">
{{ t('files_sharing', 'Note:') }}
</p>
<p class="note-to-recipient__text" v-text="note" />
</NcNoteCard>
</template>

<script setup lang="ts">
import type { Folder } from '@nextcloud/files'
import { getCurrentUser } from '@nextcloud/auth'
import { t } from '@nextcloud/l10n'
import { computed, ref } from 'vue'
import NcNoteCard from '@nextcloud/vue/dist/Components/NcNoteCard.js'
import NcUserBubble from '@nextcloud/vue/dist/Components/NcUserBubble.js'
const folder = ref<Folder>()
const note = computed<string>(() => folder.value?.attributes.note ?? '')
const user = computed(() => {
const id = folder.value?.attributes?.['owner-id']
const displayName = folder.value?.attributes?.['owner-display-name']
if (id !== getCurrentUser()?.uid) {
return {
id,
displayName,
}
}
return null
})
/**
* Update the current folder
* @param newFolder the new folder to show note for
*/
function updateFolder(newFolder: Folder) {
folder.value = newFolder
}
defineExpose({ updateFolder })
</script>

<style scoped>
.note-to-recipient {
margin-inline: var(--row-height)
}
.note-to-recipient__text {
/* respect new lines */
white-space: pre-line;
}
.note-to-recipient__heading {
font-weight: bold;
}
@media screen and (max-width: 512px) {
.note-to-recipient {
margin-inline: var(--default-grid-baseline);
}
}
</style>
32 changes: 19 additions & 13 deletions apps/files_sharing/src/views/SharingDetailsTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,9 @@
{{ t('files_sharing', 'Note to recipient') }}
</NcCheckboxRadioSwitch>
<template v-if="writeNoteToRecipientIsChecked">
<label for="share-note-textarea">
{{ t('files_sharing', 'Enter a note for the share recipient') }}
</label>
<textarea id="share-note-textarea" :value="share.note" @input="share.note = $event.target.value" />
<NcTextArea :label="t('files_sharing', 'Note to recipient')"
:placeholder="t('files_sharing', 'Enter a note for the share recipient')"
:value.sync="share.note" />
</template>
<ExternalShareAction v-for="action in externalLinkActions"
:id="action.id"
Expand Down Expand Up @@ -247,15 +246,17 @@
<script>
import { emit } from '@nextcloud/event-bus'
import { getLanguage } from '@nextcloud/l10n'
import { Type as ShareType } from '@nextcloud/sharing'
import { ShareType } from '@nextcloud/sharing'
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import NcInputField from '@nextcloud/vue/dist/Components/NcInputField.js'
import NcPasswordField from '@nextcloud/vue/dist/Components/NcPasswordField.js'
import NcAvatar from '@nextcloud/vue/dist/Components/NcAvatar.js'
import NcDateTimePickerNative from '@nextcloud/vue/dist/Components/NcDateTimePickerNative.js'
import NcButton from '@nextcloud/vue/dist/Components/NcButton.js'
import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js'
import NcDateTimePickerNative from '@nextcloud/vue/dist/Components/NcDateTimePickerNative.js'
import NcInputField from '@nextcloud/vue/dist/Components/NcInputField.js'
import NcLoadingIcon from '@nextcloud/vue/dist/Components/NcLoadingIcon.js'
import NcPasswordField from '@nextcloud/vue/dist/Components/NcPasswordField.js'
import NcTextArea from '@nextcloud/vue/dist/Components/NcTextArea.js'
import CircleIcon from 'vue-material-design-icons/CircleOutline.vue'
import CloseIcon from 'vue-material-design-icons/Close.vue'
import EditIcon from 'vue-material-design-icons/Pencil.vue'
Expand Down Expand Up @@ -290,11 +291,12 @@ export default {
components: {
NcAvatar,
NcButton,
NcInputField,
NcPasswordField,
NcDateTimePickerNative,
NcCheckboxRadioSwitch,
NcDateTimePickerNative,
NcInputField,
NcLoadingIcon,
NcPasswordField,
NcTextArea,
CloseIcon,
CircleIcon,
EditIcon,
Expand Down Expand Up @@ -717,7 +719,7 @@ export default {
* @return {Array}
*/
externalLinkActions() {
const filterValidAction = (action) => (action.shareType.includes(ShareType.SHARE_TYPE_LINK) || action.shareType.includes(ShareType.SHARE_TYPE_EMAIL)) && action.advanced
const filterValidAction = (action) => (action.shareType.includes(ShareType.Link) || action.shareType.includes(ShareType.Email)) && action.advanced
// filter only the advanced registered actions for said link
return this.ExternalShareActions.actions
.filter(filterValidAction)
Expand Down Expand Up @@ -808,6 +810,10 @@ export default {
this.advancedSectionAccordionExpanded = true
}
if (this.share.note) {
this.writeNoteToRecipientIsChecked = true
}
},
handleShareType() {
if ('shareType' in this.share) {
Expand Down
8 changes: 6 additions & 2 deletions cypress/e2e/files_sharing/FilesSharingUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@
/* eslint-disable jsdoc/require-jsdoc */
import { triggerActionForFile } from '../files/FilesUtils'

import { EntryId as FileRequestEntryID } from '../../../apps/files_sharing/src/new/newFileRequest'

export interface ShareSetting {
read: boolean
update: boolean
delete: boolean
share: boolean
download: boolean
note: string
}

export function createShare(fileName: string, username: string, shareSettings: Partial<ShareSetting> = {}) {
Expand Down Expand Up @@ -85,6 +84,11 @@ export function updateShare(fileName: string, index: number, shareSettings: Part
}
}

if (shareSettings.note !== undefined) {
cy.findByRole('checkbox', { name: /note to recipient/i }).check({ force: true, scrollBehavior: 'nearest' })
cy.findByRole('textbox', { name: /note to recipient/i }).type(shareSettings.note)
}

cy.get('[data-cy-files-sharing-share-editor-action="save"]').click({ scrollBehavior: 'nearest' })

cy.wait('@updateShare')
Expand Down
73 changes: 73 additions & 0 deletions cypress/e2e/files_sharing/note-to-recipient.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*!
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import type { User } from '@nextcloud/cypress'
import { createShare, openSharingPanel } from './FilesSharingUtils.ts'
import { getRowForFile, navigateToFolder } from '../files/FilesUtils.ts'

describe('files_sharing: Note to recipient', { testIsolation: true }, () => {
let user: User
let sharee: User

beforeEach(() => {
cy.createRandomUser().then(($user) => {
user = $user
})
cy.createRandomUser().then(($user) => {
sharee = $user
})
})

it('displays the note to the sharee', () => {
cy.mkdir(user, '/folder')
cy.uploadContent(user, new Blob([]), 'text/plain', '/folder/file')
cy.login(user)
cy.visit('/apps/files')

// share the folder
createShare('folder', sharee.userId, { read: true, download: true, note: 'Hello, this is the note.' })

cy.logout()
// Now for the sharee
cy.login(sharee)

// visit shared files view
cy.visit('/apps/files')
navigateToFolder('folder')
cy.get('.note-to-recipient')
.should('be.visible')
.and('contain.text', 'Hello, this is the note.')
})

/**
* Regression test for https://github.com/nextcloud/server/issues/46188
*/
it('shows an existing note when editing a share', () => {
cy.mkdir(user, '/folder')
cy.login(user)
cy.visit('/apps/files')

// share the folder
createShare('folder', sharee.userId, { read: true, download: true, note: 'Hello, this is the note.' })

// reload just to be sure
cy.reload()

// open the sharing tab
openSharingPanel('folder')

cy.get('[data-cy-sidebar]').within(() => {
// Open the share
cy.get('[data-cy-files-sharing-share-actions]').first().click()
// Open the custom settings
cy.get('[data-cy-files-sharing-share-permissions-bundle="custom"]').click()

cy.findByRole('checkbox', { name: /note to recipient/i })
.and('be.checked')
cy.findByRole('textbox', { name: /note to recipient/i })
.should('be.visible')
.and('have.value', 'Hello, this is the note.')
})
})
})
2 changes: 2 additions & 0 deletions dist/2351-2351.js

Large diffs are not rendered by default.

File renamed without changes.
1 change: 1 addition & 0 deletions dist/2351-2351.js.map

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions dist/2351-2351.js.map.license
2 changes: 2 additions & 0 deletions dist/5804-5804.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 4ecd592

Please sign in to comment.