Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce more extension points and delete scopes #10758

Merged
merged 13 commits into from
May 16, 2024
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
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
9 changes: 9 additions & 0 deletions changelog/unreleased/change-remove-portal-targets
Original file line number Diff line number Diff line change
@@ -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
7 changes: 7 additions & 0 deletions changelog/unreleased/enhancement-search-extensions
Original file line number Diff line number Diff line change
@@ -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
61 changes: 36 additions & 25 deletions docs/extension-system/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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:
Expand Down Expand Up @@ -96,22 +101,22 @@ 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.
kulmann marked this conversation as resolved.
Show resolved Hide resolved

#### Extension Types

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" >}})

Expand All @@ -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.
Expand All @@ -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

Expand All @@ -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).
6 changes: 3 additions & 3 deletions docs/extension-system/extension-types/actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
6 changes: 3 additions & 3 deletions docs/extension-system/extension-types/custom-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
```
Expand Down
4 changes: 2 additions & 2 deletions docs/extension-system/extension-types/folder-view.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions docs/extension-system/extension-types/right-sidebar-panels.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ It can be found below:
interface SidebarPanelExtension<R extends Item, P extends Item, T extends Item> {
id: string
type: 'sidebarPanel'
scopes?: ExtensionScope[]
extensionPointIds?: string[]
panel: SideBarPanel<R, P, T> // 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.
Expand Down
4 changes: 2 additions & 2 deletions docs/extension-system/extension-types/search.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
"engines": {
"node": ">=18"
},
"packageManager": "pnpm@9.0.6",
"packageManager": "pnpm@9.1.1",
"volta": {
"node": "18.19.0"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down Expand Up @@ -318,9 +319,7 @@ export default defineComponent({
const extensionRegistry = useExtensionRegistry()
const extensionActions = computed(() => {
return [
...extensionRegistry
.requestExtensions<ActionExtension>('action', { scopes: ['upload-menu'] })
.map((e) => e.action)
...extensionRegistry.requestExtensions(uploadMenuExtensionPoint).map((e) => e.action)
].filter((e) => e.isVisible())
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@

<script lang="ts">
import { computed, defineComponent, PropType } from 'vue'
import { ActionExtension, useEmbedMode, useExtensionRegistry } from '@ownclouders/web-pkg'
import { useEmbedMode, useExtensionRegistry } from '@ownclouders/web-pkg'
import { Resource, SpaceResource } from '@ownclouders/web-client'
import { unref } from 'vue'
import { quickActionsExtensionPoint } from '../../extensionPoints'

export default defineComponent({
name: 'QuickActions',
Expand All @@ -39,7 +40,7 @@ export default defineComponent({

const filteredActions = computed(() => {
return unref(extensionRegistry)
.requestExtensions<ActionExtension>('action', { scopes: ['resource.quick-action'] })
.requestExtensions(quickActionsExtensionPoint)
.map((e) => e.action)
.filter(({ isVisible }) => isVisible({ space: props.space, resources: [props.item] }))
})
Expand Down
Loading