Skip to content

Commit

Permalink
Implement file locking information (#9566)
Browse files Browse the repository at this point in the history
* Implement file locking information

* Implement lock in files-table (ugly)

* Implement lock owner in buildResource

* Display lock owner

* remove lock column

* Add changelog

* Add tooltip to reflect status

* Add aria label

* Disable move, delete, rename for locked files

* Address PR issues, add lock file

* Remove lockowner from details (for now)

* Fix misreplaced code

* Now i found the right code to remove

* Add unittests

* remove unused prop in test

* Fix acceptance tests
  • Loading branch information
lookacat committed Aug 24, 2023
1 parent c79650f commit a5cff39
Show file tree
Hide file tree
Showing 16 changed files with 168 additions and 12 deletions.
6 changes: 6 additions & 0 deletions changelog/unreleased/enhancement-display-lock-information
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Enhancement: Display locking information

We've added indicators and information in case a file is locked.

https://github.com/owncloud/web/pull/9566
https://github.com/owncloud/web/issues/6682
17 changes: 15 additions & 2 deletions packages/design-system/src/components/OcResource/OcResource.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,15 @@
class="oc-resource-thumbnail"
width="40"
height="40"
v-oc-tooltip="tooltipLabelIcon"
:aria-label="tooltipLabelIcon"
/>
<oc-resource-icon v-else :resource="resource">
<oc-resource-icon
v-oc-tooltip="tooltipLabelIcon"
:aria-label="tooltipLabelIcon"
v-else
:resource="resource"
>
<template v-if="showStatusIcon" #status>
<oc-icon v-bind="statusIconAttrs" size="xsmall" />
</template>
Expand Down Expand Up @@ -216,14 +223,20 @@ export default defineComponent({
return this.resource.locked || this.resource.processing
},
tooltipLabelIcon() {
if (this.resource.locked) {
return this.$gettext('This item is locked')
}
return null
},
statusIconAttrs() {
if (this.resource.locked) {
return {
name: 'lock',
fillType: 'fill'
}
}
if (this.resource.processing) {
return {
name: 'loop-right',
Expand Down
18 changes: 16 additions & 2 deletions packages/design-system/src/components/OcTile/OcTile.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@
>
<span v-text="$gettext('Disabled')" />
</oc-tag>
<div class="oc-tile-card-preview oc-flex oc-flex-middle oc-flex-center">
<div
class="oc-tile-card-preview oc-flex oc-flex-middle oc-flex-center"
v-oc-tooltip="tooltipLabelIcon"
:aria-label="tooltipLabelIcon"
>
<div class="oc-tile-card-hover"></div>
<slot name="imageField" :item="resource">
<oc-img v-if="resource.thumbnail" class="tile-preview" :src="resource.thumbnail" />
Expand Down Expand Up @@ -76,6 +80,7 @@ import OcResource from '../OcResource/OcResource.vue'
import OcResourceIcon from '../OcResourceIcon/OcResourceIcon.vue'
import OcResourceLink from '../OcResourceLink/OcResourceLink.vue'
import OcTag from '../OcTag/OcTag.vue'
import { useGettext } from 'vue3-gettext'
export default defineComponent({
name: 'OcTile',
Expand Down Expand Up @@ -119,6 +124,7 @@ export default defineComponent({
},
emits: ['click', 'contextmenu'],
setup(props) {
const { $gettext } = useGettext()
const showStatusIcon = computed(() => {
return props.resource.locked || props.resource.processing
})
Expand All @@ -141,9 +147,17 @@ export default defineComponent({
return {}
})
const tooltipLabelIcon = computed(() => {
if (props.resource.locked) {
return $gettext('This item is locked')
}
return null
})
return {
statusIconAttrs,
showStatusIcon
showStatusIcon,
tooltipLabelIcon
}
}
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ export default defineComponent({
setup() {
const store = useStore()
const { $gettext } = useGettext()
const client = useClientService()
const copiedDirect = ref(false)
const copiedEos = ref(false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ export const useFileActionsDelete = ({ store }: { store?: Store<any> } = {}) =>
return false
}

if (resources.length === 1 && resources[0].locked) {
return false
}

if (isLocationCommonActive(router, 'files-common-search')) {
return resources.some(
(r) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,20 @@ export const useFileActionsMove = ({ store }: { store?: Store<any> } = {}) => {
!isLocationPublicActive(router, 'files-public-link') &&
!isLocationCommonActive(router, 'files-common-favorites')
) {
console.log('A')
return false
}
if (resources.length === 0) {
console.log('B')
return false
}

if (!store.getters['Files/currentFolder']) {
console.log('C')
return false
}

if (resources.length === 1 && resources[0].locked) {
return false
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,10 @@ export const useFileActionsRename = ({ store }: { store?: Store<any> } = {}) =>
return false
}

if (resources.length === 1 && resources[0].locked) {
return false
}

const renameDisabled = resources.some((resource) => {
return !resource.canRename()
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ describe('delete', () => {
resources: [{ canBeDeleted: () => false }] as Resource[],
invalidLocation: false,
expectedStatus: false
},
{
resources: [{ canBeDeleted: () => true, locked: true }] as Resource[],
invalidLocation: false,
expectedStatus: false
}
])('should be set correctly', (inputData) => {
const { wrapper } = getWrapper({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { mock, mockDeep } from 'jest-mock-extended'
import { unref } from 'vue'
import { useFileActionsMove } from 'web-app-files/src/composables'
import { Resource, SpaceResource } from 'web-client/src'
import { useStore } from 'web-pkg/src/composables'
import {
RouteLocation,
createStore,
defaultComponentMocks,
defaultStoreMockOptions,
getComposableWrapper
} from 'web-test-helpers/src'

describe('move', () => {
describe('computed property "actions"', () => {
describe('move isEnabled property of returned element', () => {
it.each([
{
resources: [{ isReceivedShare: () => true, canBeDeleted: () => true }] as Resource[],
expectedStatus: true
},
{
resources: [
{ isReceivedShare: () => true, canBeDeleted: () => true, locked: true }
] as Resource[],
expectedStatus: false
}
])('should be set correctly', (inputData) => {
const { wrapper } = getWrapper({
setup: () => {
const store = useStore()
const { actions } = useFileActionsMove({ store })

const resources = inputData.resources
expect(unref(actions)[0].isEnabled({ space: null, resources })).toBe(
inputData.expectedStatus
)
}
})
})
})
})
})
function getWrapper({
setup
}: {
setup: (
instance: ReturnType<typeof useFileActionsMove>,
{
storeOptions
}: {
storeOptions: typeof defaultStoreMockOptions
}
) => void
}) {
const routeName = 'files-spaces-generic'
const mocks = {
...defaultComponentMocks({ currentRoute: mock<RouteLocation>({ name: routeName }) }),
space: mockDeep<SpaceResource>({
webDavPath: 'irrelevant'
})
}

const storeOptions = {
...defaultStoreMockOptions
}
storeOptions.modules.Files.getters.currentFolder.mockImplementation(() => mocks.space)
const store = createStore(storeOptions)
return {
mocks,
storeOptions,
wrapper: getComposableWrapper(
() => {
const store = useStore()
const instance = useFileActionsMove({ store })
setup(instance, { storeOptions })
},
{
mocks,
provide: mocks,
store
}
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ describe('rename', () => {
{
resources: [{ canRename: () => true }, { canRename: () => true }] as Resource[],
expectedStatus: false
},
{
resources: [{ canRename: () => true, locked: true }] as Resource[],
expectedStatus: false
}
])('should be set correctly', (inputData) => {
const { wrapper } = getWrapper({
Expand Down
8 changes: 8 additions & 0 deletions packages/web-client/src/helpers/resource/functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ export function buildResource(resource): Resource {
if (!resourcePath.startsWith('/')) {
resourcePath = `/${resourcePath}`
}

const lock = resource.fileInfo[DavProperty.LockDiscovery]
let activeLock
if (lock) {
activeLock = lock[DavProperty.ActiveLock]
}
const id = resource.fileInfo[DavProperty.FileId]
const r = {
id,
Expand All @@ -98,6 +104,8 @@ export function buildResource(resource): Resource {
webDavPath: resource.name,
type: isFolder ? 'folder' : resource.type,
isFolder,
locked: activeLock ? true : false,
lockOwner: activeLock ? activeLock[DavProperty.LockOwner] : '',
processing: resource.processing || false,
mdate: resource.fileInfo[DavProperty.LastModifiedDate],
size: isFolder
Expand Down
1 change: 1 addition & 0 deletions packages/web-client/src/helpers/resource/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export interface Resource {
status?: number
processing?: boolean
locked?: boolean
lockOwner?: string
spaceRoles?: {
[k: string]: SpaceRole[]
}
Expand Down
6 changes: 6 additions & 0 deletions packages/web-client/src/webdav/constants/dav.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ export abstract class DavProperty {
static readonly ETag: string = '{DAV:}getetag'
static readonly MimeType: string = '{DAV:}getcontenttype'
static readonly ResourceType: string = '{DAV:}resourcetype'
static readonly LockDiscovery: string = '{DAV:}lockdiscovery'
static readonly LockOwner: string = '{DAV:}owner'
static readonly ActiveLock: string = '{DAV:}activelock'
static readonly DownloadURL: string = '{http://owncloud.org/ns}downloadURL'
static readonly Highlights: string = '{http://owncloud.org/ns}highlights'

Expand Down Expand Up @@ -58,6 +61,9 @@ export abstract class DavProperties {
DavProperty.FileId,
DavProperty.FileParent,
DavProperty.Name,
DavProperty.LockDiscovery,
DavProperty.ActiveLock,
DavProperty.LockOwner,
DavProperty.OwnerId,
DavProperty.OwnerDisplayName,
DavProperty.ShareId,
Expand Down
2 changes: 1 addition & 1 deletion packages/web-runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"luxon": "^2.4.0",
"marked": "^4.0.12",
"oidc-client-ts": "^2.1.0",
"owncloud-sdk": "~3.1.0-alpha.7",
"owncloud-sdk": "3.1.0-alpha.8",
"p-queue": "^6.6.2",
"pinia": "^2.1.3",
"portal-vue": "3.0.0",
Expand Down
10 changes: 5 additions & 5 deletions pnpm-lock.yaml

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

Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,3 @@ Feature: Locks
Then file "lorem.txt" should not be listed on the webUI
Examples:
| lockscope |
| exclusive |
| shared |

0 comments on commit a5cff39

Please sign in to comment.