Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions apps/files/src/services/FileInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export default function(node: Node) {
fileInfo.get = (key) => fileInfo[key]
fileInfo.isDirectory = () => fileInfo.mimetype === 'httpd/unix-directory'
fileInfo.canEdit = () => Boolean(fileInfo.permissions & OC.PERMISSION_UPDATE)
fileInfo.node = node

return fileInfo
}
4 changes: 3 additions & 1 deletion apps/files/src/views/Sidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@
</NcAppSidebar>
</template>
<script lang="ts">
import type { INode } from '@nextcloud/files'

import { davRemoteURL, davRootPath, File, Folder, formatFileSize } from '@nextcloud/files'
import { defineComponent } from 'vue'
import { emit, subscribe, unsubscribe } from '@nextcloud/event-bus'
Expand Down Expand Up @@ -159,7 +161,7 @@ export default defineComponent({
error: null,
loading: true,
fileInfo: null,
node: null,
node: null as INode | null,
isFullScreen: false,
hasLowHeight: false,
}
Expand Down
32 changes: 28 additions & 4 deletions apps/files_sharing/src/components/SharingEntryLink.vue
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,16 @@
<NcActionSeparator />

<!-- external actions -->
<ExternalShareAction v-for="action in externalLinkActions"
<NcActionButton v-for="action in sortedExternalShareActions"
:key="action.id"
@click="action.exec(share, fileInfo.node)">
<template #icon>
<NcIconSvgWrapper :svg="action.iconSvg" />
</template>
{{ action.label(share, fileInfo.node) }}
</NcActionButton>

<SidebarTabExternalActionLegacy v-for="action in externalLegacyShareActions"
:id="action.id"
:key="action.id"
:action="action"
Expand Down Expand Up @@ -230,6 +239,8 @@ import { t } from '@nextcloud/l10n'
import moment from '@nextcloud/moment'
import { generateUrl, getBaseUrl } from '@nextcloud/router'
import { ShareType } from '@nextcloud/sharing'
import { getSidebarInlineActions } from '@nextcloud/sharing/ui'
import { toRaw } from 'vue'

import VueQrcode from '@chenfengyuan/vue-qrcode'
import NcActionButton from '@nextcloud/vue/components/NcActionButton'
Expand All @@ -255,8 +266,8 @@ import PlusIcon from 'vue-material-design-icons/Plus.vue'

import SharingEntryQuickShareSelect from './SharingEntryQuickShareSelect.vue'
import ShareExpiryTime from './ShareExpiryTime.vue'
import SidebarTabExternalActionLegacy from './SidebarTabExternal/SidebarTabExternalActionLegacy.vue'

import ExternalShareAction from './ExternalShareAction.vue'
import GeneratePassword from '../utils/GeneratePassword.ts'
import Share from '../models/Share.ts'
import SharesMixin from '../mixins/SharesMixin.js'
Expand All @@ -267,7 +278,6 @@ export default {
name: 'SharingEntryLink',

components: {
ExternalShareAction,
NcActions,
NcActionButton,
NcActionCheckbox,
Expand All @@ -290,6 +300,7 @@ export default {
PlusIcon,
SharingEntryQuickShareSelect,
ShareExpiryTime,
SidebarTabExternalActionLegacy,
},

mixins: [SharesMixin, ShareDetails],
Expand Down Expand Up @@ -323,6 +334,7 @@ export default {

ExternalLegacyLinkActions: OCA.Sharing.ExternalLinkActions.state,
ExternalShareActions: OCA.Sharing.ExternalShareActions.state,
externalShareActions: getSidebarInlineActions(),

// tracks whether modal should be opened or not
showQRCode: false,
Expand Down Expand Up @@ -568,13 +580,25 @@ export default {
*
* @return {Array}
*/
externalLinkActions() {
externalLegacyShareActions() {
const filterValidAction = (action) => (action.shareType.includes(ShareType.Link) || action.shareType.includes(ShareType.Email)) && !action.advanced
// filter only the registered actions for said link
console.error('external legacy', this.ExternalShareActions, this.ExternalShareActions.actions.filter(filterValidAction))
return this.ExternalShareActions.actions
.filter(filterValidAction)
},

/**
* Additional actions for the menu
*
* @return {import('@nextcloud/sharing/ui').ISidebarInlineAction[]}
*/
sortedExternalShareActions() {
return this.externalShareActions
.filter((action) => action.enabled(toRaw(this.share), toRaw(this.fileInfo.node)))
.sort((a, b) => a.order - b.order)
},

isPasswordPolicyEnabled() {
return typeof this.config.passwordPolicy === 'object'
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<!--
- SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->

<template>
<component :is="action.element"
:key="action.id"
ref="actionElement"
:share.prop="share"
:node.prop="node"
:on-save.prop="onSave" />
</template>

<script lang="ts" setup>
import type { INode } from '@nextcloud/files'
import type { IShare } from '@nextcloud/sharing'
import type { ISidebarAction } from '@nextcloud/sharing/ui'
import type { PropType } from 'vue'

import { ref, toRaw, watchEffect } from 'vue'

const props = defineProps({
action: {
type: Object as PropType<ISidebarAction>,
required: true,
},
node: {
type: Object as PropType<INode>,
required: true,
},
share: {
type: Object as PropType<IShare | undefined>,
required: true,
},
})

defineExpose({ save })

const actionElement = ref()
const savingCallback = ref()

watchEffect(() => {
if (!actionElement.value) {
return
}

// This seems to be only needed in Vue 2 as the .prop modifier does not really work on the vue 2 version of web components
// TODO: Remove with Vue 3
actionElement.value.node = toRaw(props.node)
actionElement.value.onSave = onSave
actionElement.value.share = toRaw(props.share)
})

/**
* The share is reset thus save the state of the component.
*/
async function save() {
await savingCallback.value?.()
}

/**
* Vue does not allow to call methods on wrapped web components
* so we need to pass it per callback.
*
* @param callback - The callback to be called on save
*/
function onSave(callback: () => Promise<void>) {
savingCallback.value = callback
}
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@
-->

<template>
<Component :is="data.is"
<component :is="data.is"
v-bind="data"
v-on="action.handlers">
{{ data.text }}
</Component>
</component>
</template>

<script>
import Share from '../models/Share.ts'
import Share from '../../models/Share.ts'

export default {
name: 'ExternalShareAction',
name: 'SidebarTabExternalActionLegacy',

props: {
id: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!--
- SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->

<template>
<component :is="section.element" ref="sectionElement" :node.prop="node" />
</template>

<script lang="ts" setup>
import type { INode } from '@nextcloud/files'
import type { ISidebarSection } from '@nextcloud/sharing/ui'
import type { PropType } from 'vue'

import { ref, watchEffect } from 'vue'

const props = defineProps({
node: {
type: Object as PropType<INode>,
required: true,
},
section: {
type: Object as PropType<ISidebarSection>,
required: true,
},
})

// TOOD: Remove with Vue 3
const sectionElement = ref()
watchEffect(() => {
sectionElement.value.node = props.node
})
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!--
- SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
- SPDX-License-Identifier: AGPL-3.0-or-later
-->

<template>
<div class="sharing-tab-external-section-legacy">
<component :is="component" :file-info="fileInfo" />
</div>
</template>

<script lang="ts" setup>
import { computed, type Component, type PropType } from 'vue'

const props = defineProps({
fileInfo: {
type: Object,
required: true,
},
sectionCallback: {
type: Function as PropType<(el: HTMLElement | undefined, fileInfo: unknown) => Component>,
required: true,
},
})

const component = computed(() => props.sectionCallback(undefined, props.fileInfo))
</script>

<style scoped>
.sharing-tab-external-section-legacy {
width: 100%;
}
</style>
8 changes: 5 additions & 3 deletions apps/files_sharing/src/services/ExternalLinkActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import logger from './logger.ts'

export default class ExternalLinkActions {

_state
Expand All @@ -13,7 +15,7 @@ export default class ExternalLinkActions {

// init default values
this._state.actions = []
console.debug('OCA.Sharing.ExternalLinkActions initialized')
logger.debug('OCA.Sharing.ExternalLinkActions initialized')
}

/**
Expand All @@ -35,13 +37,13 @@ export default class ExternalLinkActions {
* @return {boolean}
*/
registerAction(action) {
OC.debug && console.warn('OCA.Sharing.ExternalLinkActions is deprecated, use OCA.Sharing.ExternalShareAction instead')
logger.warn('OCA.Sharing.ExternalLinkActions is deprecated, use `registerSidebarAction` from `@nextcloud/sharing` instead')

if (typeof action === 'object' && action.icon && action.name && action.url) {
this._state.actions.push(action)
return true
}
console.error('Invalid action provided', action)
logger.error('Invalid action provided', action)
return false
}

Expand Down
10 changes: 7 additions & 3 deletions apps/files_sharing/src/services/ExternalShareActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import logger from './logger.ts'

export default class ExternalShareActions {

_state
Expand All @@ -13,7 +15,7 @@ export default class ExternalShareActions {

// init default values
this._state.actions = []
console.debug('OCA.Sharing.ExternalShareActions initialized')
logger.debug('OCA.Sharing.ExternalShareActions initialized')
}

/**
Expand Down Expand Up @@ -44,21 +46,23 @@ export default class ExternalShareActions {
* @return {boolean}
*/
registerAction(action) {
logger.warn('OCA.Sharing.ExternalShareActions is deprecated, use `registerSidebarAction` from `@nextcloud/sharing` instead')

// Validate action
if (typeof action !== 'object'
|| typeof action.id !== 'string'
|| typeof action.data !== 'function' // () => {disabled: true}
|| !Array.isArray(action.shareType) // [\@nextcloud/sharing.Types.Link, ...]
|| typeof action.handlers !== 'object' // {click: () => {}, ...}
|| !Object.values(action.handlers).every(handler => typeof handler === 'function')) {
console.error('Invalid action provided', action)
logger.error('Invalid action provided', action)
return false
}

// Check duplicates
const hasDuplicate = this._state.actions.findIndex(check => check.id === action.id) > -1
if (hasDuplicate) {
console.error(`An action with the same id ${action.id} already exists`, action)
logger.error(`An action with the same id ${action.id} already exists`, action)
return false
}

Expand Down
Loading
Loading