diff --git a/changelog/9.0.0_2024-04-16/enhancement-extension-points b/changelog/9.0.0_2024-04-16/change-extension-points similarity index 76% rename from changelog/9.0.0_2024-04-16/enhancement-extension-points rename to changelog/9.0.0_2024-04-16/change-extension-points index b552f468823..a9cb2d39ce0 100644 --- a/changelog/9.0.0_2024-04-16/enhancement-extension-points +++ b/changelog/9.0.0_2024-04-16/change-extension-points @@ -1,4 +1,6 @@ -Enhancement: Add extensionPoint concept +Change: Add extensionPoint concept + +BREAKING CHANGE for developers: The `scopes` property has been removed from the `Extension` type in favour of the new `extensionPointIds` property. The extension system now allows developers to register extension points. An extension point defines the metadata for the integration of a certain extension type in a certain context. Examples for extension points are render targets for @@ -7,10 +9,11 @@ menu), etc. Extensions can now specify that they are only valid for a certain or multiple extension points. This way a file action extension can e.g. specify to be rendered only in the context menu, but not in the batch actions. Consequently, the extension points -concept is the next iteration of the `scopes` concept. The `scopes` concept will most likely be removed in a future release. +concept is the next iteration of the `scopes` concept. The `scopes` concept has been removed from the codebase. Extension points can define if users should be able to choose preferences for the extension point. E.g. for the global progress bar extension point, users can choose which of the available progress bar extensions should be used, since the extension point -only allows one extension to be active. At the moment we persist the user choice in the local storage of the browser. +only allows one extension to be active. At the moment we persist the user choice in the localStorage of the browser. https://github.com/owncloud/web/pull/10443 +https://github.com/owncloud/web/pull/10758 diff --git a/changelog/9.0.0_2024-04-16/change-remove-deprecated-quick-actions-extension-point b/changelog/9.0.0_2024-04-16/change-remove-deprecated-quick-actions-extension-point index 0ca62b1ca33..0f287c4657c 100644 --- a/changelog/9.0.0_2024-04-16/change-remove-deprecated-quick-actions-extension-point +++ b/changelog/9.0.0_2024-04-16/change-remove-deprecated-quick-actions-extension-point @@ -1,6 +1,6 @@ Change: Remove deprecated extension point for adding quick actions -BREAKING CHANGE for developers: The old way of registering quick actions via the `quickaction` property of an app has been removed. Quick actions should be registered as extension via our extension registry. They need to be of type `action` and have the `files.quick-action` scope. +BREAKING CHANGE for developers: The old way of registering quick actions via the `quickaction` property of an app has been removed. Quick actions should be registered as extension via our extension registry. They need to be of type `action` and have the `app.files.quick-action` extensionPointId. https://github.com/owncloud/web/pull/10102 https://github.com/owncloud/web/pull/10223 diff --git a/changelog/unreleased/change-disable-opening-files-in-embed-mode b/changelog/unreleased/change-disable-opening-files-in-embed-mode index f3cba0584b2..7c23230625d 100644 --- a/changelog/unreleased/change-disable-opening-files-in-embed-mode +++ b/changelog/unreleased/change-disable-opening-files-in-embed-mode @@ -1,6 +1,6 @@ Change: Disable opening files in embed mode -We have disabled to open files in the embed mode, since opening or editing files is not in scope of the embed mode +We have disabled to open files in the embed mode, since opening or editing files is not in scope of the embed mode. https://github.com/owncloud/web/pull/10786 https://github.com/owncloud/web/issues/10635 diff --git a/changelog/unreleased/change-remove-portal-targets b/changelog/unreleased/change-remove-portal-targets new file mode 100644 index 00000000000..352e96562b6 --- /dev/null +++ b/changelog/unreleased/change-remove-portal-targets @@ -0,0 +1,9 @@ +Change: Portal target removed + +BREAKING CHANGE for developers: The (undocumented) portal target `app.runtime.header` is not available anymore. Please use the extension point `app.runtime.header.center` with `customComponent` extensions instead (for details see below). + +The portal target `app.runtime.header` has been removed in favour of a new extension point with the id `app.runtime.header.center`. The extension point +is capable of mounting extensions of type `customComponent`. The search bar, which was previously using this portal target, was rewired into an extension. Other `portal` instances which used this portal target won't work anymore +and need to be ported to the `customComponent` extension type instead. + +https://github.com/owncloud/web/pull/10758 diff --git a/changelog/unreleased/enhancement-search-extensions b/changelog/unreleased/enhancement-search-extensions new file mode 100644 index 00000000000..663b33d3e1f --- /dev/null +++ b/changelog/unreleased/enhancement-search-extensions @@ -0,0 +1,7 @@ +Enhancement: Search providers extension point + +We've added a new extension point with the id `app.search.provider` and for the extensionType `search` that can be used to +register additional search providers. All search providers that are registered for this extension point will be used +by the global search automatically. + +https://github.com/owncloud/web/pull/10758 diff --git a/docs/extension-system/_index.md b/docs/extension-system/_index.md index 69eab32ccd7..46886612814 100644 --- a/docs/extension-system/_index.md +++ b/docs/extension-system/_index.md @@ -12,7 +12,7 @@ geekdocCollapseSection: true ## Concepts and Building Blocks -The ownCloud Web can be extended through various entry points with custom **apps** and **extensions**. +ownCloud Web can be extended through various entry points with custom **apps** and **extensions**. ### Distinction between Apps and Extensions @@ -27,6 +27,11 @@ It serves two main purposes: Both parts are optional. This means that an application can be a file editor without any custom extensions, or even contain no custom application code at all and only host extensions to be registered in the extension registry, or a combination of both. +### Examples + +You can find open source examples for apps and extensions in our [curated list of ownCloud apps and extensions](https://github.com/owncloud/awesome-ocis). +Feel free to contribute or just be inspired for your own apps or extensions. + ### Apps To get started, define a `src/index.ts`. Below is the most basic example of its content: @@ -96,7 +101,7 @@ In contrast to applications, extensions usually have a rather small scope and de The globally available extension registry provided by the ownCloud Web runtime can be used to both register and query extensions. All extensions which are being made available via an `app` get registered in the extension registry automatically. In your custom application code you can -then query any of the available extensions by providing a `type` string and optionally a list of `scopes`. Throughout the ownCloud Web platform +then query any of the available extensions by providing an `extensionPoint` entity. Throughout the ownCloud Web platform and most prominently also in the `files` app we have defined some extension points which automatically use certain extensions, see the `Extension Points` section below. @@ -104,14 +109,14 @@ and most prominently also in the `files` app we have defined some extension poin For building an extension you can choose from the types predefined by the ownCloud Web extension system. See the full list of available extension types below. -1. `ActionExtension` (type `action`) - An extension that can register `Action` items which then get shown in various places (e.g. context menus, batch actions), depending on their -respective scope. Most commonly used for file and folder actions (e.g. copy, rename, delete, etc.). For details, please refer to the [action docs]({{< ref "extension-types/actions.md" >}}) -2. `SearchExtension` (type `search`) - An extension that can register additional search providers. For details, please refer to the [search docs]({{< ref "extension-types/search.md" >}}) -3. `SidebarNavExtension` (type `sidebarNav`) - An extension that can register additional navigation items to the left sidebar. These can be scoped to specific apps, and programmatically enabled/disabled. -For details, please refer to the [sidebar nav docs]({{< ref "extension-types/left-sidebar-menu-item.md" >}}) -4. `SidebarPanelExtension`, (type `sidebarPanel`) - An extension that can register panels to the right sidebar. For details, please refer to the [sidebar panel docs]({{< ref "extension-types/right-sidebar-panels.md" >}}) -5. `FolderViewExtension` (type `folderView`) - An extension that can register additional ways of displaying the content of a folder (resources, so spaces, folders or files) to the user. -For details, please refer to the [folder view docs]({{< ref "extension-types/folder-view.md" >}}) +1. `ActionExtension` (type `action`) - An extension that can register `Action` items which then get shown in various places (e.g. context menus, batch actions), depending on the +extension points referenced in the extension respectively. Most commonly used for file and folder actions (e.g. copy, rename, delete, etc.). For details, please refer to the [action docs]({{< ref "extension-types/actions.md" >}}). +2. `SearchExtension` (type `search`) - An extension that can register additional search providers. For details, please refer to the [search docs]({{< ref "extension-types/search.md" >}}). +3. `SidebarNavExtension` (type `sidebarNav`) - An extension that can register additional navigation items for the left sidebar. These can be scoped to specific apps, and programmatically enabled/disabled. +For details, please refer to the [sidebar nav docs]({{< ref "extension-types/left-sidebar-menu-item.md" >}}). +4. `SidebarPanelExtension`, (type `sidebarPanel`) - An extension that can register panels for the right sidebar. For details, please refer to the [sidebar panel docs]({{< ref "extension-types/right-sidebar-panels.md" >}}). +5. `FolderViewExtension` (type `folderView`) - An extension that can register additional ways of displaying the content of a folder (resources like spaces, folders or files) to the user. +For details, please refer to the [folder view docs]({{< ref "extension-types/folder-view.md" >}}). 6. `CustomComponentExtension` (type `customComponent`) - An extension that can register a custom component for a render target. For details, please refer to the [custom component docs]({{< ref "extension-types/custom-components.md" >}}) @@ -120,7 +125,7 @@ that an important extension type is missing and would be beneficial for the plat #### Extension Base Configuration -Any extension is required to define at least an `id` and a `type`. +Any extension is required to define at least an `id` and a `type` in order to fulfill the generic `Extension` interface. The `id` is supposed to be unique throughout the ownCloud Web ecosystem. In order to keep `id`s readable for humans we didn't want to enforce uniqueness through e.g. uuids. Instead, we chose to use dot-formatted namespaces like e.g. `com.github.owncloud.web.files.search`. We'd like to encourage you to follow the same format for your own extensions. @@ -133,18 +138,24 @@ You can find predefined extension point ids in the extension points section belo #### Extension Points -There are standardized components and places where extensions are being used automatically. The following are the ones that are currently provided -by the ownCloud Web runtime or the `files` app. - -1. Left Sidebar for Navigation -2. Right Sidebar in any file(s) context -3. Folder Views in the files app -4. Right click context menu in the files app -5. Batch actions in the files app -6. Upload menu in the files app -7. Quick actions in the files list of the files app -8. Search results in the search app -9. Global progress bar for the global loading state. Extension point id `app.runtime.global-progress-bar`. Allows to render a single custom component. +There are standardized components and places where extensions are being used automatically. The following ones are currently provided by the ownCloud Web runtime or +the `files` app. If you decide to develop an extension which fulfills the type and registers itself for the extensionPointId of the respective extension point, +your extension will be used automatically. + +1. Left Sidebar for Navigation. ExtensionPointId `app.${appName}.navItems` (dynamically created for each app). Mounts extensions of type `sidebarNav`. +2. Global top bar + 1. Center area. ExtensionPointId `app.runtime.header.center`. Mounts extensions of type `customComponent`. + 2. Progress bar for the global loading state. ExtensionPointId `app.runtime.global-progress-bar`. Mounts a single extensions of type `customComponent`. If multiple exist, the user can choose via the account page. +3. Files app + 1. Right sidebar. ExtensionPointId `app.files.sidebar`. Mounts extensions of type `sidebarPanel`. Used in any file(s) context (files app, file viewer apps, file editor apps). + 2. Folder views for regular folders. ExtensionPointId `app.files.folder-views.folder`. Mounts extensions of type `folderView`. + 3. Folder views for the project spaces overview. ExtensionPointId `app.files.folder-views.project-spaces`. Mounts extensions of type `folderView`. + 4. Folder views for the favorites page. ExtensionPointId `app.files.folder-views.favorites`. Mounts extensions of type `folderView`. + 5. Right click context menu. ExtensionPointId `app.files.context-actions`. Mounts extensions of type `action`. + 6. Batch actions in the app bar above file lists. ExtensionPointId `app.files.batch-actions`. Mounts extensions of type `action`. + 7. Upload menu. ExtensionPointId `app.files.upload-menu`. Mounts extensions of type `action`. + 8. Quick actions. ExtensionPointId `app.files.quick-actions`. Mounts extensions of type `action`. +4. Global search providers. ExtensionPointId `app.search.providers`. Utilizes extensions of type `search` as search engines for the search input in the global top bar. #### User Preferences for Extensions @@ -154,7 +165,7 @@ The user can then select one out of all the extensions which have been registere ### Helpful packages -We currently offer two packages that can be integrated into your app, providing useful utilities and types. +We currently offer the following packages that can be integrated into your app, providing useful utilities and types. -- `web-client` - This package serves as an abstraction layer between the server APIs and an app or extension. It converts API data into objects with helpful types and utilities. For details, please refer to the package's [README.md](https://github.com/owncloud/web/blob/master/packages/web-client/README.md). +- `web-client` - This package serves as an abstraction layer between the server APIs and an app or extension. It converts raw API data into objects with helpful types and utilities. For details, please refer to the package's [README.md](https://github.com/owncloud/web/blob/master/packages/web-client/README.md). - `web-pkg` - This package provides utilities, most importantly a variety of components and composables, that can be useful when developing apps and extensions. For details, please refer to the package's [README.md](https://github.com/owncloud/web/blob/master/packages/web-pkg/README.md). diff --git a/docs/extension-system/extension-types/actions.md b/docs/extension-system/extension-types/actions.md index 7dd456ade1d..ea7119b37ea 100644 --- a/docs/extension-system/extension-types/actions.md +++ b/docs/extension-system/extension-types/actions.md @@ -16,18 +16,18 @@ Actions are one of the possible extension types. Registered actions get rendered ### Configuration -This is what the ActionExtension interface looks like: +This is what the `ActionExtension` interface looks like: ```typescript interface ActionExtension { id: string type: 'action' - scopes?: ExtensionScope[] + extensionPointIds?: string[] action: Action // Please check the Action section below } ``` -For `id`, `type`, and `scopes`, please see [extension base section]({{< ref "../_index.md#extension-base-configuration" >}}) in top level docs. +For `id`, `type`, and `extensionPointIds`, please see [extension base section]({{< ref "../_index.md#extension-base-configuration" >}}) in top level docs. #### Action diff --git a/docs/extension-system/extension-types/custom-components.md b/docs/extension-system/extension-types/custom-components.md index b9f5a528991..17a582100c2 100644 --- a/docs/extension-system/extension-types/custom-components.md +++ b/docs/extension-system/extension-types/custom-components.md @@ -22,9 +22,9 @@ Here's what it looks like: ```typescript interface CustomComponentExtension { - id: string, - type: 'customComponent', - extensionPointIds: string[], + id: string + type: 'customComponent' + extensionPointIds?: string[] content: Slot | Component } ``` diff --git a/docs/extension-system/extension-types/folder-view.md b/docs/extension-system/extension-types/folder-view.md index 3aa6995b483..b9000f7295c 100644 --- a/docs/extension-system/extension-types/folder-view.md +++ b/docs/extension-system/extension-types/folder-view.md @@ -19,13 +19,13 @@ This is what the FolderViewExtension interface looks like: ```typescript interface FolderViewExtension { id: string - scopes?: ExtensionScope[] type: 'folderView' + extensionPointIds?: string[] folderView: FolderView // See FolderView section below } ``` -For `id`, `type`, and `scopes`, please see [extension base section]({{< ref "../_index.md#extension-base-configuration" >}}) in top level docs. +For `id`, `type`, and `extensionPointIds`, please see [extension base section]({{< ref "../_index.md#extension-base-configuration" >}}) in top level docs. #### FolderView diff --git a/docs/extension-system/extension-types/left-sidebar-menu-item.md b/docs/extension-system/extension-types/left-sidebar-menu-item.md index 5fe949064ac..50d6c45ed8a 100644 --- a/docs/extension-system/extension-types/left-sidebar-menu-item.md +++ b/docs/extension-system/extension-types/left-sidebar-menu-item.md @@ -23,13 +23,13 @@ It looks like this: interface SidebarNavExtension { id: string type: 'sidebarNav' - scopes?: ExtensionScope[] + extensionPointIds?: string[] navItem: AppNavigationItem // Please check the AppNavigationItem section below } } ``` -For `id`, `type`, and `scopes`, please see [extension base section]({{< ref "../_index.md#extension-base-configuration" >}}) in top level docs. +For `id`, `type`, and `extensionPointIds`, please see [extension base section]({{< ref "../_index.md#extension-base-configuration" >}}) in top level docs. #### AppNavigationItem diff --git a/docs/extension-system/extension-types/right-sidebar-panels.md b/docs/extension-system/extension-types/right-sidebar-panels.md index 15565b36530..5c595952f73 100644 --- a/docs/extension-system/extension-types/right-sidebar-panels.md +++ b/docs/extension-system/extension-types/right-sidebar-panels.md @@ -29,12 +29,12 @@ It can be found below: interface SidebarPanelExtension { id: string type: 'sidebarPanel' - scopes?: ExtensionScope[] + extensionPointIds?: string[] panel: SideBarPanel // Please check the SideBarPanel section below } ``` -For `id`, `type`, and `scopes`, please see [extension base section]({{< ref "../_index.md#extension-base-configuration" >}}) in the top level docs. +For `id`, `type`, and `extensionPointIds`, please see [extension base section]({{< ref "../_index.md#extension-base-configuration" >}}) in the top level docs. The `panel` object configures the actual sidebar panel. It consists of different properties and functions, where all the functions get called with a `SideBarPanelContext` entity from the integrating extension points. diff --git a/docs/extension-system/extension-types/search.md b/docs/extension-system/extension-types/search.md index 7a46b37fd2d..0f2be7b5615 100644 --- a/docs/extension-system/extension-types/search.md +++ b/docs/extension-system/extension-types/search.md @@ -23,7 +23,7 @@ An example of a search extension configuration can be found below: interface SearchExtension { id: string type: 'search' - scopes?: ExtensionScope[] + extensionPointIds?: string[] searchProvider: { id: string available: boolean @@ -34,7 +34,7 @@ interface SearchExtension { } ``` -For `id`, `type`, and `scopes`, please see [extension base section]({{< ref "../_index.md#extension-base-configuration" >}}) in top level docs. +For `id`, `type`, and `extensionPointIds`, please see [extension base section]({{< ref "../_index.md#extension-base-configuration" >}}) in top level docs. The `searchProvider` object configures the actual provider. It consist of the following: diff --git a/package.json b/package.json index 94fe885d602..c19e3020168 100644 --- a/package.json +++ b/package.json @@ -114,7 +114,7 @@ "engines": { "node": ">=18" }, - "packageManager": "pnpm@9.0.6", + "packageManager": "pnpm@9.1.1", "volta": { "node": "18.19.0" }, diff --git a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue index 9986cc69ef6..9f77e8837b4 100644 --- a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue +++ b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue @@ -224,10 +224,11 @@ import { useService, useUpload, UppyService, UploadResult } from '@ownclouders/w import { HandleUpload } from 'web-app-files/src/HandleUpload' import { useRoute } from 'vue-router' import { useGettext } from 'vue3-gettext' -import { ActionExtension, useExtensionRegistry } from '@ownclouders/web-pkg' +import { useExtensionRegistry } from '@ownclouders/web-pkg' import { Action, ResourceIcon } from '@ownclouders/web-pkg' import { v4 as uuidv4 } from 'uuid' import { storeToRefs } from 'pinia' +import { uploadMenuExtensionPoint } from '../../extensionPoints' export default defineComponent({ components: { @@ -318,9 +319,7 @@ export default defineComponent({ const extensionRegistry = useExtensionRegistry() const extensionActions = computed(() => { return [ - ...extensionRegistry - .requestExtensions('action', { scopes: ['upload-menu'] }) - .map((e) => e.action) + ...extensionRegistry.requestExtensions(uploadMenuExtensionPoint).map((e) => e.action) ].filter((e) => e.isVisible()) }) diff --git a/packages/web-app-files/src/components/FilesList/QuickActions.vue b/packages/web-app-files/src/components/FilesList/QuickActions.vue index 8cb860691de..32999323a06 100644 --- a/packages/web-app-files/src/components/FilesList/QuickActions.vue +++ b/packages/web-app-files/src/components/FilesList/QuickActions.vue @@ -17,9 +17,10 @@