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
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