Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: owncloud/web
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v11.2.0
Choose a base ref
...
head repository: owncloud/web
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v11.3.0
Choose a head ref
  • 6 commits
  • 24 files changed
  • 2 contributors

Commits on Feb 25, 2025

  1. feat: add psec file icon

    LukasHirt committed Feb 25, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    LukasHirt Lukas Hirt
    Copy the full SHA
    a959552 View commit details
  2. feat: handle editor-less extensions

    When an app does not expose any routes, mark it as editor-less so that we do not try to load editor actions for such extension.
    LukasHirt committed Feb 25, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    LukasHirt Lukas Hirt
    Copy the full SHA
    79c8c55 View commit details
  3. Verified

    This commit was signed with the committer’s verified signature.
    LukasHirt Lukas Hirt
    Copy the full SHA
    8033e69 View commit details
  4. add tests for creating password-protected-folder (#12192)

    Signed-off-by: nabim777 <nabinalemagar019@gmail.com>
    nabim777 authored and LukasHirt committed Feb 25, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    LukasHirt Lukas Hirt
    Copy the full SHA
    d85b339 View commit details
  5. chore: bump version to 11.3.0

    LukasHirt committed Feb 25, 2025

    Verified

    This commit was signed with the committer’s verified signature.
    LukasHirt Lukas Hirt
    Copy the full SHA
    e360853 View commit details
  6. Merge pull request #12215 from owncloud/chore/release-11.3.0

    [full-ci] chore: release 11.3.0
    LukasHirt authored Feb 25, 2025

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    1d251f1 View commit details
Showing with 156 additions and 26 deletions.
  1. +5 −0 changelog/11.3.0_2025-02-25/enhancement-add-custom-handler-to-file-extensions.md
  2. +5 −0 changelog/11.3.0_2025-02-25/enhancement-add-psec-file-icon.md
  3. +5 −0 changelog/11.3.0_2025-02-25/enhancement-handle-extensions-without-editor.md
  4. +5 −0 docs/extension-system/viewer-editor-apps.md
  5. +1 −1 package.json
  6. +1 −1 packages/design-system/package.json
  7. +1 −0 packages/design-system/src/assets/icons/file-psec-fill.svg
  8. +1 −1 packages/eslint-config/package.json
  9. +1 −1 packages/extension-sdk/package.json
  10. +1 −1 packages/web-client/package.json
  11. +1 −1 packages/web-pkg/package.json
  12. +9 −1 packages/web-pkg/src/apps/types.ts
  13. +1 −0 packages/web-pkg/src/composables/actions/files/useFileActions.ts
  14. +4 −1 packages/web-pkg/src/composables/actions/files/useFileActionsCreateNewFile.ts
  15. +2 −1 packages/web-pkg/src/composables/piniaStores/apps.ts
  16. +4 −0 packages/web-pkg/src/helpers/resource/icon.ts
  17. +11 −2 packages/web-pkg/tests/unit/composables/actions/files/useFileActions.spec.ts
  18. +23 −8 packages/web-pkg/tests/unit/composables/actions/files/useFileActionsCreateNewFile.spec.ts
  19. +4 −1 packages/web-runtime/src/container/application/classic.ts
  20. +1 −1 packages/web-test-helpers/package.json
  21. +10 −1 tests/drone/config-ocis.json
  22. +24 −0 tests/e2e/cucumber/features/file-action/passswordProtectedFolder.feature
  23. +2 −1 tests/e2e/cucumber/steps/ui/resources.ts
  24. +34 −3 tests/e2e/support/objects/app-files/resource/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Enhancement: Add custom handler to file extensions

We've added a new property into application file extensions called `customHandler`. This property allows app developers to use completely custom flows when creating new files.

https://github.com/owncloud/web/pull/12109
5 changes: 5 additions & 0 deletions changelog/11.3.0_2025-02-25/enhancement-add-psec-file-icon.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Enhancement: Add psec file icon

We've added a new icon which will be used for psec files. These files are representing our password protected folders.

https://github.com/owncloud/web/pull/12104
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Enhancement: Handle extensions without editor

We've added a new property to extensions that asserts whether is has an editor or not. This property is computed by checking whether the extension exposes any routes.

https://github.com/owncloud/web/pull/12105
5 changes: 5 additions & 0 deletions docs/extension-system/viewer-editor-apps.md
Original file line number Diff line number Diff line change
@@ -119,5 +119,10 @@ interface ApplicationFileExtension {
mimeType?: string
newFileMenu?: { menuTitle: () => string }
routeName?: string
customHandler? (
fileActionOptions: FileActionOptions,
extension: string,
appFileExtension: ApplicationFileExtension
) => Promise<void> | void
}
```
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "11.2.0",
"version": "11.3.0",
"private": true,
"homepage": "https://github.com/owncloud/web",
"license": "AGPL-3.0",
2 changes: 1 addition & 1 deletion packages/design-system/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ownclouders/design-system",
"version": "11.2.0",
"version": "11.3.0",
"description": "ownCloud Design System is based on VueDesign Systems and is used to design ownCloud UI components",
"keywords": [
"vue design system",
1 change: 1 addition & 0 deletions packages/design-system/src/assets/icons/file-psec-fill.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/eslint-config/package.json
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
"name": "@ownclouders/eslint-config",
"main": "index.js",
"private": false,
"version": "11.2.0",
"version": "11.3.0",
"license": "AGPL-3.0",
"type": "module",
"author": "ownCloud GmbH <devops@owncloud.com>",
2 changes: 1 addition & 1 deletion packages/extension-sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ownclouders/extension-sdk",
"version": "11.2.0",
"version": "11.3.0",
"description": "ownCloud Web Extension SDK",
"license": "AGPL-3.0",
"main": "index.mjs",
2 changes: 1 addition & 1 deletion packages/web-client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ownclouders/web-client",
"version": "11.2.0",
"version": "11.3.0",
"description": "ownCloud web client",
"license": "AGPL-3.0",
"private": false,
2 changes: 1 addition & 1 deletion packages/web-pkg/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ownclouders/web-pkg",
"version": "11.2.0",
"version": "11.3.0",
"description": "ownCloud web pkg",
"license": "AGPL-3.0",
"main": "src/index.ts",
10 changes: 9 additions & 1 deletion packages/web-pkg/src/apps/types.ts
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import { Extension, ExtensionPoint } from '../composables/piniaStores'
import { IconFillType } from '../helpers'
import { Resource, SpaceResource } from '@ownclouders/web-client'
import { Translations } from 'vue3-gettext'
import { FileActionOptions } from '../composables/actions/types'

export interface AppReadyHookArgs {
globalProperties: ComponentCustomProperties & Record<string, any>
@@ -63,6 +64,11 @@ export interface ApplicationFileExtension {
newFileMenu?: { menuTitle: () => string }
routeName?: string
secureView?: boolean
customHandler?: (
fileActionOptions: FileActionOptions,
extension: string,
appFileExtension: ApplicationFileExtension
) => Promise<void> | void
}

/** ApplicationInformation describes required information of an application */
@@ -86,6 +92,8 @@ export interface ApplicationInformation {
translations?: Translations
/** @deprecated */
applicationMenu?: ApplicationMenuItem
/** Asserts whether the app has any route which works as an editor */
hasEditor?: boolean
}

/**
@@ -99,7 +107,7 @@ export interface ApplicationTranslations {

/** ClassicApplicationScript reflects classic application script structure */
export interface ClassicApplicationScript {
appInfo?: ApplicationInformation
appInfo?: Omit<ApplicationInformation, 'hasEditor'>
routes?: ((args: ComponentCustomProperties) => RouteRecordRaw[]) | RouteRecordRaw[]
navItems?: ((args: ComponentCustomProperties) => AppNavigationItem[]) | AppNavigationItem[]
translations?: ApplicationTranslations
Original file line number Diff line number Diff line change
@@ -112,6 +112,7 @@ export const useFileActions = () => {
}

return appsStore.fileExtensions
.filter((fileExtension) => appsStore.apps[fileExtension.app]?.hasEditor)
.map((fileExtension): FileAction => {
const appInfo = appsStore.apps[fileExtension.app]

Original file line number Diff line number Diff line change
@@ -184,7 +184,10 @@ export const useFileActionsCreateNewFile = ({ space }: { space?: Ref<SpaceResour
actions.push({
name: 'create-new-file',
icon: 'add',
handler: (args) => handler(args, appFileExtension.extension, appFileExtension),
handler: (args) =>
appFileExtension.customHandler
? appFileExtension.customHandler(args, appFileExtension.extension, appFileExtension)
: handler(args, appFileExtension.extension, appFileExtension),
label: () => $gettext(appFileExtension.newFileMenu.menuTitle()),
isVisible: () => {
return unref(currentFolder)?.canUpload({ user: userStore.user })
3 changes: 2 additions & 1 deletion packages/web-pkg/src/composables/piniaStores/apps.ts
Original file line number Diff line number Diff line change
@@ -52,7 +52,8 @@ export const useAppsStore = defineStore('apps', () => {
data.hasPriority ||
unref(externalAppConfig)?.[appId]?.priorityExtensions?.includes(data.extension) ||
false,
secureView: data.secureView || false
secureView: data.secureView || false,
customHandler: data.customHandler || null
})
}

4 changes: 4 additions & 0 deletions packages/web-pkg/src/helpers/resource/icon.ts
Original file line number Diff line number Diff line change
@@ -212,6 +212,10 @@ const fileIcon = {
board: {
icon: { name: 'resource-type-board' },
extensions: ['ggs']
},
psec: {
icon: { name: 'file-psec', color: 'var(--oc-color-icon-folder)' },
extensions: ['psec']
}
}

Original file line number Diff line number Diff line change
@@ -90,13 +90,22 @@ function getWrapper({ setup }: { setup: (instance: ReturnType<typeof useFileActi
{
extension: 'txt'
}
]
],
hasEditor: true
},
external: {
defaultExtension: '',
icon: 'check_box_outline_blank',
name: 'External',
id: 'external'
id: 'external',
hasEditor: true
},
'editor-less': {
defaultExtension: '',
icon: 'check_box_outline_blank',
name: 'Editor Less',
id: 'editor-less',
hasEditor: false
}
},
fileExtensions: [
Original file line number Diff line number Diff line change
@@ -68,16 +68,37 @@ describe('useFileActionsCreateNewFile', () => {
})
})
})

describe('customHandler', () => {
it('should use customHandler when it is provided', () => {
const action = mock<ApplicationFileExtension>({ app: 'link-opener', customHandler: vi.fn() })

getWrapper({
action,
setup: ({ actions }) => {
unref(actions).at(0).handler()
expect(action.customHandler).toHaveBeenCalled()
}
})
})
})
})

function getWrapper({
resolveCreateFile = true,
space = undefined,
setup
setup,
action = mock<ApplicationFileExtension>({
app: 'text-editor',
extension: '.txt',
newFileMenu: { menuTitle: vi.fn() },
customHandler: null
})
}: {
resolveCreateFile?: boolean
space?: SpaceResource
setup: (instance: ReturnType<typeof useFileActionsCreateNewFile>) => void
action?: ApplicationFileExtension
}) {
const mocks = {
...defaultComponentMocks({
@@ -111,13 +132,7 @@ function getWrapper({
pluginOptions: {
piniaOptions: {
appsState: {
fileExtensions: [
mock<ApplicationFileExtension>({
app: 'text-editor',
extension: '.txt',
newFileMenu: { menuTitle: vi.fn() }
})
]
fileExtensions: [action]
},
resourcesStore: { currentFolder }
}
5 changes: 4 additions & 1 deletion packages/web-runtime/src/container/application/classic.ts
Original file line number Diff line number Diff line change
@@ -110,7 +110,10 @@ export const convertClassicApplication = ({
})

const appsStore = useAppsStore()
appsStore.registerApp(applicationScript.appInfo, applicationScript.translations)
appsStore.registerApp(
{ ...applicationScript.appInfo, hasEditor: applicationScript.routes?.length > 0 },
applicationScript.translations
)

if (applicationScript.extensions) {
extensionRegistry.registerExtensions(applicationScript.extensions)
2 changes: 1 addition & 1 deletion packages/web-test-helpers/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ownclouders/web-test-helpers",
"version": "11.2.0",
"version": "11.3.0",
"description": "ownCloud web test helpers",
"license": "AGPL-3.0",
"private": false,
11 changes: 10 additions & 1 deletion tests/drone/config-ocis.json
Original file line number Diff line number Diff line change
@@ -7,5 +7,14 @@
"client_id": "web",
"response_type": "code"
},
"apps": ["files", "text-editor", "preview", "pdf-viewer", "search", "admin-settings", "ocm"]
"apps": [
"files",
"text-editor",
"preview",
"pdf-viewer",
"search",
"admin-settings",
"ocm",
"password-protected-folders"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Feature: create folder with password
As a user
I want to create password protected folder
So that I can control folder access


Scenario: create a password-protected folder
Given "Admin" creates following user using API
| id |
| Alice |
And "Alice" has logged in
When "Alice" creates the following resources
| resource | type | password |
| sampleFolder | Password Protected Folder | %public% |
And "Alice" enables the option to display the hidden file
Then following resources should be displayed in the files list for user "Alice"
| resource |
| .PasswordProtectedFolders |
| sampleFolder.psec |
When "Alice" opens folder ".PasswordProtectedFolders/projects/Personal"
Then following resources should be displayed in the files list for user "Alice"
| resource |
| sampleFolder |
And "Alice" logs out
3 changes: 2 additions & 1 deletion tests/e2e/cucumber/steps/ui/resources.ts
Original file line number Diff line number Diff line change
@@ -26,7 +26,8 @@ When(
await resourceObject.create({
name: info.resource,
type: info.type as createResourceTypes,
content: info.content
content: info.content,
password: info.password
})
}
}
37 changes: 34 additions & 3 deletions tests/e2e/support/objects/app-files/resource/actions.ts
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ import { dragDropFiles } from '../../../utils/dragDrop'
import { LinksEnvironment } from '../../../environment'
import { config } from '../../../../config'
import { buildXpathLiteral } from '../../../utils/locator'
import { securePassword } from '../../../store'

const topbarFilenameSelector = '#app-top-bar-resource .oc-resource-name'
const downloadFileButtonSingleShareView = '.oc-files-actions-download-file-trigger'
@@ -36,6 +37,7 @@ const breadcrumbLastResourceNameSelector = '.oc-breadcrumb-item-text-last'
const breadcrumbResourceSelector = '//*[@id="files-breadcrumb"]//span[text()=%s]//ancestor::li'
const addNewResourceButton = `#new-file-menu-btn`
const createNewFolderButton = '#new-folder-btn'
const passwordProtectedFolderButton = '.new-file-btn-psec'
const createNewTxtFileButton = '.new-file-btn-txt'
const createNewMdFileButton = '.new-file-btn-md'
const createNewOfficeDocumentFileBUtton = '//div[@id="new-file-menu-drop"]//span[text()="%s"]'
@@ -46,6 +48,8 @@ const textEditor = '#text-editor #text-editor-container'
const textEditorPlainTextInput = '#text-editor #text-editor-container .cm-content'
const textEditorMarkdownInput = '#text-editor #text-editor-container .cm-content'
const resourceNameInput = '.oc-modal input'
const passwordProtectedFolderNameInput = '#input-folder-name'
const passwordProtectedFolderPasswordInput = '#input-folder-password'
const resourceUploadButton = '#upload-menu-btn'
const fileUploadInput = '#files-file-upload-input'
const folderUploadInput = '#files-folder-upload-input'
@@ -194,12 +198,14 @@ export type createResourceTypes =
| 'mdFile'
| 'OpenDocument'
| 'Microsoft Word'
| 'Password Protected Folder'

export interface createResourceArgs {
page: Page
name: string
type: createResourceTypes
content?: string
password?: string
}

export const createSpaceFromFolder = async ({
@@ -274,14 +280,39 @@ export const createNewFolder = async ({
])
}

export const createPasswordProtectedFolder = async ({
page,
resource,
password
}: {
page: Page
resource: string
password: string
}): Promise<void> => {
password = password === '%public%' ? securePassword : password
await page.locator(passwordProtectedFolderButton).click()
await page.locator(passwordProtectedFolderNameInput).fill(resource)
await page.locator(passwordProtectedFolderPasswordInput).fill(password)
await Promise.all([
page.waitForResponse((resp) => resp.status() === 201 && resp.request().method() === 'MKCOL'),
page.waitForResponse((resp) => resp.status() === 200 && resp.request().method() === 'POST'),
page.waitForResponse((resp) => resp.status() === 207 && resp.request().method() === 'PROPFIND'),
page.locator(util.format(actionConfirmationButton, 'Create')).click()
])
}

export const createNewFileOrFolder = async (args: createResourceArgs): Promise<void> => {
const { page, name, type, content } = args
const { page, name, type, content, password } = args
await page.locator(addNewResourceButton).click()
switch (type) {
case 'folder': {
await createNewFolder({ page, resource: name })
break
}
case 'Password Protected Folder': {
await createPasswordProtectedFolder({ page, resource: name, password: password })
break
}
case 'txtFile': {
await page.locator(createNewTxtFileButton).click()
await page.locator(resourceNameInput).fill(name)
@@ -479,7 +510,7 @@ const isAppProviderServiceForOfficeSuitesReadyInWebUI = async (page: Page, type:
}

export const createResources = async (args: createResourceArgs): Promise<void> => {
const { page, name, type, content } = args
const { page, name, type, content, password } = args
const paths = name.split('/')
const resource = paths.pop()

@@ -495,7 +526,7 @@ export const createResources = async (args: createResourceArgs): Promise<void> =
}
await clickResource({ page, path })
}
await createNewFileOrFolder({ page, name: resource, type, content })
await createNewFileOrFolder({ page, name: resource, type, content, password })
}

export const editTextDocument = async ({