Skip to content

Commit

Permalink
[full-ci] Sharing jail (#6593)
Browse files Browse the repository at this point in the history
* Fix unit tests

* Code cleanup in folder service and tasks

* Rename "isEnabled" to "isActive" in folder loaders

* make loader tasks context aware (capabilities)

* Add share routes in spaces namespace

* Route shares into a new view for spaces-aware backend

* Add error logging to folder service task execution

* Share loader task

* Introduce personal space resource loader

* Load share root in new view

* Fix share view breadcrumb

* Fix path and selection handling in SharedResource view

* relative path handling for shares

* Fix file and folder creation inside share

* Include SharedResource route in active state of Shares nav item

* Make file actions available on share routes

* Switch location picker to personal space dav endpoint

* Include current route query in breadcrumb items

As of now we want to keep all query options active when navigating
up/down a folder hierarchy (sorting, pagination options, and now also
the shareId for the SharedResource view). We even have code to restore
query options if they are not set. This commit fixes navigation in
SharedResource breadcrumbs (those NEED the shareId, otherwise we can't
construct the correct webdav path at the moment) and also reduces the
occurrences of reloading query options from the local storage (because
they are already set via the breadcrumb now).

* Add resourceId to whitelisted query items

* Rename resourceId query option to shareId

* Make use of share_jail feature flag in capabilities.spaces

* Fix sharing quick action availability

* Fix navigating into shares immediately after accept

* Fix unit tests

* Fix showing shares in right sidebar on shared with me page

* Fix non-share jail shared with me page

* Capability usage cleanup after rebase

* Fix SharedResource view after rebase

* Remove reportGenerator/cucumber_report.json from repo and ignore

* Fix share name in right sidebar (shared with me page)

* Fix uploads in share jail shares

* Code cleanup

* Fix folder uploads in shares

* Disable renaming of shares for share jail until fixed in backend

* Fix Open Folder action for shares

* Exclude pending and declined shares for editor app open actions

* Fix parent folder links in resource table

* fix ocis kindergarten e2e test journey

* REVERT: Skip unit&e2e tests

* Adjust tests for sharing jail

* Use personal space for fetching file info after upload

* Load newly created files/folders via personal space

* bring back ci e2e tests
fix failing e2e tests
fix clickResource e2e test helper

* fix starlark

* Skip last resharing test in oCIS CI

Co-authored-by: Florian Schade <[email protected]>
Co-authored-by: Pascal Wengerter <[email protected]>
  • Loading branch information
3 people authored May 6, 2022
1 parent ec17961 commit b747d29
Show file tree
Hide file tree
Showing 75 changed files with 1,330 additions and 346 deletions.
2 changes: 1 addition & 1 deletion .drone.star
Original file line number Diff line number Diff line change
Expand Up @@ -717,7 +717,7 @@ def checkTestSuites():
if (type(items) == "list"):
suites += items
elif (type(items) == "string"):
suites += [key]
suites.append(key)
else:
print("Error: invalid value for suite, it must be a list or string")
return False
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ dist

reports
tests/e2e/cucumber/report/cucumber_report.json
tests/e2e/cucumber/reportGenerator/cucumber_report.json
tests/ocis
tests/testing-app

Expand Down
82 changes: 76 additions & 6 deletions packages/web-app-files/src/components/AppBar/CreateAndUpload.vue
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,16 @@ import MixinFileActions, { EDITOR_MODE_CREATE } from '../../mixins/fileActions'
import { buildResource, buildWebDavFilesPath, buildWebDavSpacesPath } from '../../helpers/resources'
import { isLocationPublicActive, isLocationSpacesActive } from '../../router'
import { useActiveLocation } from '../../composables'
import { useAppDefaults } from 'web-pkg/src/composables'
import { useAppDefaults, useCapabilityShareJailEnabled } from 'web-pkg/src/composables'
import { DavProperties, DavProperty } from 'web-pkg/src/constants'
import ResourceUpload from './Upload/ResourceUpload.vue'
import { defineComponent, getCurrentInstance, onMounted } from '@vue/composition-api'
import { UppyResource, useUpload } from 'web-runtime/src/composables/upload'
import { useUploadHelpers } from '../../composables/upload'
import { SHARE_JAIL_ID } from '../../services/folder'
import { clientService } from 'web-pkg/src/services'
export default defineComponent({
components: {
Expand Down Expand Up @@ -153,9 +155,11 @@ export default defineComponent({
isPublicLocation: useActiveLocation(isLocationPublicActive, 'files-public-files'),
isSpacesProjectsLocation: useActiveLocation(isLocationSpacesActive, 'files-spaces-projects'),
isSpacesProjectLocation: useActiveLocation(isLocationSpacesActive, 'files-spaces-project'),
isSpacesShareLocation: useActiveLocation(isLocationSpacesActive, 'files-spaces-share'),
...useAppDefaults({
applicationName: 'files'
})
}),
hasShareJail: useCapabilityShareJailEnabled()
}
},
data: () => ({
Expand Down Expand Up @@ -263,11 +267,27 @@ export default defineComponent({
let resource
if (this.isPersonalLocation) {
path = buildWebDavFilesPath(this.user.id, path)
if (this.hasShareJail) {
const graphClient = clientService.graphAuthenticated(
this.configuration.server,
this.getToken
)
const userResponse = await graphClient.users.getMe()
if (!userResponse.data) {
console.error('graph.user.getMe() has no data')
return
}
path = buildWebDavSpacesPath(userResponse.data.id, path || '')
} else {
path = buildWebDavFilesPath(this.user.id, path)
}
resource = await this.$client.files.fileInfo(path, DavProperties.Default)
} else if (this.isSpacesProjectLocation) {
path = buildWebDavSpacesPath(this.$route.params.storageId, path)
resource = await this.$client.files.fileInfo(path, DavProperties.Default)
} else if (this.isSpacesShareLocation) {
path = buildWebDavSpacesPath([SHARE_JAIL_ID, this.$route.query.shareId].join('!'), path)
resource = await this.$client.files.fileInfo(path, DavProperties.Default)
} else {
resource = await this.$client.publicFiles.getFileInfo(
path,
Expand Down Expand Up @@ -379,13 +399,30 @@ export default defineComponent({
let resource
if (this.isPersonalLocation) {
path = buildWebDavFilesPath(this.user.id, path)
if (this.hasShareJail) {
const graphClient = clientService.graphAuthenticated(
this.configuration.server,
this.getToken
)
const userResponse = await graphClient.users.getMe()
if (!userResponse.data) {
console.error('graph.user.getMe() has no data')
return
}
path = buildWebDavSpacesPath(userResponse.data.id, path || '')
} else {
path = buildWebDavFilesPath(this.user.id, path)
}
await this.$client.files.createFolder(path)
resource = await this.$client.files.fileInfo(path, DavProperties.Default)
} else if (this.isSpacesProjectLocation) {
path = buildWebDavSpacesPath(this.$route.params.storageId, path)
await this.$client.files.createFolder(path)
resource = await this.$client.files.fileInfo(path, DavProperties.Default)
} else if (this.isSpacesShareLocation) {
path = buildWebDavSpacesPath([SHARE_JAIL_ID, this.$route.query.shareId].join('!'), path)
await this.$client.files.createFolder(path)
resource = await this.$client.files.fileInfo(path, DavProperties.Default)
} else {
await this.$client.publicFiles.createFolder(path, null, this.publicLinkPassword)
resource = await this.$client.publicFiles.getFileInfo(
Expand Down Expand Up @@ -468,13 +505,30 @@ export default defineComponent({
let path = pathUtil.join(this.currentPath, fileName)
if (this.isPersonalLocation) {
path = buildWebDavFilesPath(this.user.id, path)
if (this.hasShareJail) {
const graphClient = clientService.graphAuthenticated(
this.configuration.server,
this.getToken
)
const userResponse = await graphClient.users.getMe()
if (!userResponse.data) {
console.error('graph.user.getMe() has no data')
return
}
path = buildWebDavSpacesPath(userResponse.data.id, path || '')
} else {
path = buildWebDavFilesPath(this.user.id, path)
}
await this.$client.files.putFileContents(path, '')
resource = await this.$client.files.fileInfo(path, DavProperties.Default)
} else if (this.isSpacesProjectLocation) {
path = buildWebDavSpacesPath(this.$route.params.storageId, path)
await this.$client.files.putFileContents(path, '')
resource = await this.$client.files.fileInfo(path, DavProperties.Default)
} else if (this.isSpacesShareLocation) {
path = buildWebDavSpacesPath([SHARE_JAIL_ID, this.$route.query.shareId].join('!'), path)
await this.$client.files.putFileContents(path, '')
resource = await this.$client.files.fileInfo(path, DavProperties.Default)
} else {
await this.$client.publicFiles.putFileContents('', path, this.publicLinkPassword, '')
resource = await this.$client.publicFiles.getFileInfo(
Expand Down Expand Up @@ -542,11 +596,27 @@ export default defineComponent({
let resource
let path = pathUtil.join(this.currentPath, fileName)
if (this.isPersonalLocation) {
path = buildWebDavFilesPath(this.user.id, path)
if (this.hasShareJail) {
const graphClient = clientService.graphAuthenticated(
this.configuration.server,
this.getToken
)
const userResponse = await graphClient.users.getMe()
if (!userResponse.data) {
console.error('graph.user.getMe() has no data')
return
}
path = buildWebDavSpacesPath(userResponse.data.id, path || '')
} else {
path = buildWebDavFilesPath(this.user.id, path)
}
resource = await this.$client.files.fileInfo(path, DavProperties.Default)
} else if (this.isSpacesProjectLocation) {
path = buildWebDavSpacesPath(this.$route.params.storageId, path)
resource = await this.$client.files.fileInfo(path, DavProperties.Default)
} else if (this.isSpacesShareLocation) {
path = buildWebDavSpacesPath([SHARE_JAIL_ID, this.$route.query.shareId].join('!'), path)
resource = await this.$client.files.fileInfo(path, DavProperties.Default)
} else {
resource = await this.$client.publicFiles.getFileInfo(
path,
Expand Down
90 changes: 69 additions & 21 deletions packages/web-app-files/src/components/FilesList/ResourceTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -171,13 +171,23 @@ import { EVENT_TROW_MOUNTED, EVENT_FILE_DROPPED } from '../../constants'
import { SortDir } from '../../composables'
import * as path from 'path'
import { determineSortFields } from '../../helpers/ui/resourceTable'
import { useCapabilitySpacesEnabled } from 'web-pkg/src/composables'
import {
useCapabilityProjectSpacesEnabled,
useCapabilityShareJailEnabled
} from 'web-pkg/src/composables'
import Rename from '../../mixins/actions/rename'
import { defineComponent, PropType } from '@vue/composition-api'
import { extractDomSelector, Resource } from '../../helpers/resource'
import { ShareTypes } from '../../helpers/share'
import { createLocationSpaces } from '../../router'
const mapResourceFields = (resource: Resource, mapping = {}) => {
return Object.keys(mapping).reduce((result, resourceKey) => {
result[mapping[resourceKey]] = resource[resourceKey]
return result
}, {})
}
export default defineComponent({
mixins: [Rename],
model: {
Expand Down Expand Up @@ -251,6 +261,33 @@ export default defineComponent({
required: false,
default: null
},
/**
* Maps resource values to route params. Use `{ resourceFieldName: 'routeParamName' }` as format.
*
* An example would be `{ id: 'fileId' }` to map the value of the `id` field of a resource
* to the `fileId` param of the target route.
*
* Defaults to `{ storageId: 'storageId' } to map the value of the `storageId` field of a resource
* to the `storageId` param of the target route.
*/
targetRouteParamMapping: {
type: Object,
required: false,
default: () => ({ storageId: 'storageId' })
},
/**
* Maps resource values to route query options. Use `{ resourceFieldName: 'routeQueryName' }` as format.
*
* An example would be `{ id: 'fileId' }` to map the value of the `id` field of a resource
* to the `fileId` query option of the target route.
*
* Defaults to an empty object because no query options are expected as default.
*/
targetRouteQueryMapping: {
type: Object,
required: false,
default: () => ({})
},
/**
* Asserts whether clicking on the resource name triggers any action
*/
Expand Down Expand Up @@ -342,7 +379,8 @@ export default defineComponent({
},
setup() {
return {
hasSpaces: useCapabilitySpacesEnabled()
hasShareJail: useCapabilityShareJailEnabled(),
hasProjectSpaces: useCapabilityProjectSpacesEnabled()
}
},
data() {
Expand Down Expand Up @@ -532,32 +570,40 @@ export default defineComponent({
this.openWithPanel('sharing-item')
},
folderLink(file) {
return this.createFolderLink(file.path, file.storageId)
return this.createFolderLink(file.path, file, false)
},
parentFolderLink(file) {
return this.createFolderLink(path.dirname(file.path), file.storageId)
return this.createFolderLink(path.dirname(file.path), file, true)
},
createFolderLink(path, storageId) {
createFolderLink(path, resource, parentFolder) {
if (this.targetRoute === null) {
return {}
}
const matchingSpace = this.getMatchingSpace(storageId)
const params = {
item: path.replace(/^\//, '') || '/',
...this.targetRoute.params,
...mapResourceFields(resource, this.targetRouteParamMapping)
}
const query = {
...this.targetRoute.query,
...mapResourceFields(resource, this.targetRouteQueryMapping)
}
if (matchingSpace && matchingSpace?.driveType === 'project') {
return createLocationSpaces('files-spaces-project', {
params: { storageId, item: path.replace(/^\//, '') || undefined }
})
if (this.hasProjectSpaces) {
const matchingSpace = this.getMatchingSpace(resource.storageId)
if (matchingSpace?.driveType === 'project') {
return createLocationSpaces('files-spaces-project', {
params,
query
})
}
}
return {
name: this.targetRoute.name,
query: this.targetRoute.query,
params: {
item: path.replace(/^\//, '') || undefined,
...this.targetRoute.params,
...(storageId && { storageId })
}
params,
query
}
},
fileDragged(file) {
Expand Down Expand Up @@ -705,13 +751,15 @@ export default defineComponent({
return this.spaces.find((space) => space.id === storageId)
},
getDefaultParentFolderName(resource) {
if (!this.hasSpaces) {
return this.$gettext('All files and folders')
if (this.hasProjectSpaces) {
const matchingSpace = this.getMatchingSpace(resource.storageId)
if (matchingSpace?.driveType === 'project') {
return matchingSpace.name
}
}
const matchingSpace = this.getMatchingSpace(resource.storageId)
if (matchingSpace && matchingSpace?.driveType === 'project') {
return matchingSpace.name
if (!this.hasShareJail) {
return this.$gettext('All files and folders')
}
return this.$gettext('Personal')
Expand Down
6 changes: 3 additions & 3 deletions packages/web-app-files/src/components/Search/Preview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import Vue from 'vue'
import { mapGetters, mapState } from 'vuex'
import { createLocationSpaces } from '../../router'
import path from 'path'
import { useCapabilitySpacesEnabled } from 'web-pkg/src/composables'
import { useCapabilityShareJailEnabled } from 'web-pkg/src/composables'
const visibilityObserver = new VisibilityObserver()
Expand All @@ -44,7 +44,7 @@ export default {
},
setup() {
return {
hasSpaces: useCapabilitySpacesEnabled(),
hasShareJail: useCapabilityShareJailEnabled(),
resourceTargetLocation: createLocationSpaces('files-spaces-personal-home'),
resourceTargetLocationSpace: createLocationSpaces('files-spaces-project')
}
Expand All @@ -62,7 +62,7 @@ export default {
return this.spaces.find((space) => space.id === this.resource.storageId)
},
defaultParentFolderName() {
if (!this.hasSpaces) {
if (!this.hasShareJail) {
return this.$gettext('All files and folders')
}
Expand Down
13 changes: 13 additions & 0 deletions packages/web-app-files/src/components/SideBar/SideBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { computed, defineComponent } from '@vue/composition-api'
import FileInfo from './FileInfo.vue'
import SpaceInfo from './SpaceInfo.vue'
import { useCapabilityShareJailEnabled } from 'web-pkg/src/composables'
export default defineComponent({
components: { FileInfo, SpaceInfo, SideBar },
Expand All @@ -42,6 +43,12 @@ export default defineComponent({
}
},
setup() {
return {
hasShareJail: useCapabilityShareJailEnabled()
}
},
data() {
return {
focused: undefined,
Expand Down Expand Up @@ -108,6 +115,9 @@ export default defineComponent({
// root path `/` like for personal home doesn't exist for public links
return pathSegments.length === 1
}
if (isLocationSharesActive(this.$router, 'files-shares-with-me')) {
return !this.highlightedFile
}
return !pathSegments.length
},
highlightedFileThumbnail() {
Expand Down Expand Up @@ -172,6 +182,9 @@ export default defineComponent({
this.highlightedFile.webDavPath,
DavProperties.Default
)
if (this.hasShareJail && isLocationSharesActive(this.$router, 'files-shares-with-me')) {
item.name = this.highlightedFile.name
}
}
this.selectedFile = buildResource(item)
Expand Down
Loading

0 comments on commit b747d29

Please sign in to comment.