Reference providers

Reference providers are related with two Nextcloud features:

Link previews were introduced in Nextcloud 25. Apps can register a reference provider to add preview support for some HTTP links. To provide link previews, a reference provider needs to:

The Smart Picker was introduced in Nextcloud 26. This is a user interface component allowing users to search or generate links from various places in Nextcloud like Text, Talk, Collectives, Notes, Mail... Reference providers can be implemented so they appear in the Smart Picker's provider list. The Smart Picker can use 2 types of providers:

In summary, reference providers can be registered by apps to

This documentation explains how to

Use the Smart Picker in your app

There are 3 ways to make the Smart Picker appear in your app:

Just like for the link previews, you need to dispatch the OCP\Collaboration\Reference\RenderReferenceEvent event before loading the page in which you want to show the Smart Picker.

NcRichContenteditable

The Smart Picker is integrated in the NcRichContenteditable Vue component. It is enabled by default but can be disabled by setting the linkAutocomplete prop to false.

The picker provider list opens when the user types the "/" character. The picker result then gets directly inserted in the content.

NcRichContenteditable component documentation

NcReferencePickerModal

You display the Smart Picker by using the NcReferencePickerModal Vue component. It is available in the Nextcloud Vue library.

import { NcReferencePickerModal } from '@nextcloud/vue/dist/Components/NcRichText.js'

Available props:

  • initialProvider (optional): If a reference provider object is passed, skip the provider selection and directly show this provider

  • focusOnCreate (optional, default: true): Focus on the main input element on creation

  • isInsideViewer (optional, default: false): Set this to true if NcReferencePickerModal is used inside the Viewer. This tells the Viewer to deal with the focus trap.

getLinkWithPicker

To display the Smart Picker outside Vue, you can use the getLinkWithPicker helper function. It takes 2 parameters:

  • providerId (optional, default: null): The provider to select in the picker. If null, the provider selection is displayed first.

  • isInsideViewer (optional): This will be passed internally to NcReferencePickerModal as the isInsideViewer prop.

This function returns a promise that resolves with the picker result. This promise is rejected if the user closes the Smart Picker.

import { getLinkWithPicker } from '@nextcloud/vue/dist/Components/NcRichText.js'

getLinkWithPicker(null, true)
    .then(result => {
        console.debug('Smart Picker result', result)
    })
    .catch(error => {
        console.error('Smart Picker promise rejected', error)
    })

Use the Smart Picker in clients

Clients can partially support the Smart Picker features.

There are 2 types of Smart Picker providers:

As the custom picker components are web components, clients might not be able (or want) to render them. So we are mostly interested in the second type here: The ones using unified search providers.

In Nextcloud's web UI, those providers are rendered with a generic search Vue component which shows a search input, lists the search result in a dropdown menu and directly submits the URL of the selected result. The search is done by directly querying the Unified Search OCS API. This is described later.

To implement something similar to the Smart Picker in a client, it is required to know how to:

Get the provider list

The list of Smart Picker providers can be obtained via an OCS endpoint. Each provider object contains the list of supported unified search providers.

Here is the server endpoint to list the smart picker providers:

curl -u USER:PASSWD -H "Accept: application/json" -H "ocs-apirequest: true" \
    "https://my.nextcloud.org/ocs/v2.php/references/providers"

and an example response:

{
  "ocs": {
    "meta": {
      "status": "ok",
      "statuscode": 200,
      "message": "OK"
    },
    "data": [
      {
        "id": "github-issue-pr",
        "title": "GitHub issues, pull requests and comments",
        "icon_url": "https://my.nextcloud.org/apps/integration_github/img/app-dark.svg",
        "order": 10,
        "search_providers_ids": [
          "github-search-issues",
          "github-search-repos"
        ]
      },
      {
        "id": "openstreetmap-point",
        "title": "Map location (by OpenStreetMap)",
        "icon_url": "https://my.nextcloud.org/apps/integration_openstreetmap/img/app-dark.svg",
        "order": 10,
        "search_providers_ids": [
          "openstreetmap-search-location"
        ]
      },
      {
        "id": "files",
        "title": "Files",
        "icon_url": "https://my.nextcloud.org/apps/files/img/folder.svg",
        "order": 0
      }
    ]
  }
}

In this example, the "files" Smart Picker provider does not support any unified search provider but the "github-issue-pr" one supports 2 of them and the "openstreetmap-point" support one.

Use the Unified Search API

More details in the :ref:`Unified Search<unified-search>` documentation.

Here is how to search using the Unified Search OCS API:

curl -u USER:PASSWD -H "Accept: application/json" -H "ocs-apirequest: true" \
    "https://my.nextcloud.org/ocs/v2.php/search/providers/PROVIDER_ID/search?term=QUERY&limit=LIMIT"

# with a cursor (paginated search)
curl -u USER:PASSWD -H "Accept: application/json" -H "ocs-apirequest: true" \
    "https://my.nextcloud.org/ocs/v2.php/search/providers/PROVIDER_ID/search?term=QUERY&limit=LIMIT&cursor=CURSOR"

# search a github issue with the query "bug"
curl -u USER:PASSWD -H "Accept: application/json" -H "ocs-apirequest: true" \
    "https://my.nextcloud.org/ocs/v2.php/search/providers/github-search-issues/search?term=bug&limit=2"

Example response:

{
  "ocs": {
    "meta": {
      "status": "ok",
      "statuscode": 200,
      "message": "OK"
    },
    "data": {
      "name": "GitHub issues and pull requests",
      "isPaginated": true,
      "entries": [
        {
          "thumbnailUrl": "https://my.nextcloud.org/apps/integration_github/avatar/Daily-DAYO",
          "title": " [bug] Change Trim bugs",
          "subline": "⑁ DAYO_Android#409",
          "resourceUrl": "https://github.com/Daily-DAYO/DAYO_Android/pull/409",
          "icon": "",
          "rounded": true,
          "attributes": []
        },
        {
          "thumbnailUrl": "https://my.nextcloud.org/apps/integration_github/avatar/walinejs",
          "title": " [Bug]:  || [Bug]:",
          "subline": "⦿ waline#2014",
          "resourceUrl": "https://github.com/walinejs/waline/issues/2014",
          "icon": "",
          "rounded": true,
          "attributes": []
        }
      ],
      "cursor": 2
    }
  }
}

Update a provider last usage date

In Nextcloud's web UI, the order in which the providers are listed to the users depends on the last date they were used. The most recently used providers are displayed first.

In a client, once a provider has been used, a request to this endpoint should be done:

curl -u USER:PASSWD -H "Accept: application/json" -H "ocs-apirequest: true" -X PUT \
    "https://my.nextcloud.org/ocs/v2.php/search/provider/PROVIDER_ID"

A timestamp optional request parameter can be passed. The last usage date will be set to "now" by default.

Register a reference provider

A reference provider is a class implementing the OCP\Collaboration\Reference\IReferenceProvider interface. If you just want to resolve links, simply implement the IReferenceProvider interface. This is described in the "Resolving links" section.

To support resolving links from public shares, the OCP\Collaboration\Reference\IPublicReferenceProvider interface needs to be implemented as well.

If you want your reference provider to be used by the Smart Picker, you need to extend the OCP\Collaboration\Reference\ADiscoverableReferenceProvider class to declare all required information.

There are 2 ways to make your provider appear in the smart picker, in other words, 2 types of providers:

Extend the Smart Picker

If you want your reference provider to appear in the Smart Picker to search/get links, it needs to be discoverable (extend the OCP\Collaboration\Reference\ADiscoverableReferenceProvider abstract class) and either

This is an exclusive choice. You can't support search providers AND register a custom picker component. If you still want to mix both approaches, you can register a custom picker component which includes a custom search feature.

Extending ADiscoverableReferenceProvider implies defining those methods:

Declare supported Unified Search providers

If you want your reference provider to let users pick links from unified search results, your reference provider must implement OCP\Collaboration\Reference\ISearchableReferenceProvider and define the getSupportedSearchProviderIds method which return a list of supported search provider IDs.

Once this provider is selected in the Smart Picker, users will see a generic search interface giving results from all the search providers you declared as supported. Once a result is selected, the Smart Picker will return the associated resource URL.

Register a custom picker component

On the backend side, in your lib/AppInfo/Application.php, you should listen to the OCP\Collaboration\Reference\RenderReferenceEvent. In the corresponding listener, you should load the scripts that will register custom picker components.

In other words, when the RenderReferenceEvent event is dispatched, the Smart Picker will potentially by used in the frontend so you need to load the related scripts from your app.

You can define your own picker user interface for your provider by registering a custom picker component. This can be done with the registerCustomPickerElement function from @nextcloud/vue/dist/Components/NcRichText.js. This function takes 3 parameters:

  • The reference provider ID for which you register the custom picker component

  • The callback function to create and mount your component

  • The callback function to delete/destroy your component

The creation callback must return a NcCustomPickerRenderResult object to which you have to give the DOM element you just created and optionally an object (the Vue instance for example). This render result will be then be passed to the destroy callback to let you properly clean and delete your custom component.

To register a Vue component as a custom picker component:

import { registerCustomPickerElement, NcCustomPickerRenderResult } from '@nextcloud/vue/dist/Components/NcRichText.js'
import Vue from 'vue'
import MyCustomPickerElement from './MyCustomPickerElement.vue'

registerCustomPickerElement('REFERENCE_PROVIDER_ID', (el, { providerId, accessible }) => {
    const Element = Vue.extend(MyCustomPickerElement)
    const vueElement = new Element({
        propsData: {
            providerId,
            accessible,
        },
    }).$mount(el)
    return new NcCustomPickerRenderResult(vueElement.$el, vueElement)
}, (el, renderResult) => {
    // call the $destroy method on your custom element's Vue instance
    renderResult.object.$destroy()
})

To register anything else:

import {
    registerCustomPickerElement,
    NcCustomPickerRenderResult,
} from '@nextcloud/vue/dist/Components/NcRichText.js'

registerCustomPickerElement('REFERENCE_PROVIDER_ID', (el, { providerId, accessible }) => {
    const paragraph = document.createElement('p')
    paragraph.textContent = 'click this button to return a link'
    el.append(paragraph)
    const button = document.createElement('button')
    button.textContent = 'I am a button'
    button.addEventListener('click', () => {
        const event = new CustomEvent(
            'submit',
            {
                bubbles: true,
                detail: 'https://nextcloud.com'
            }
        )
        el.dispatchEvent(event)
    })
    el.append(button)
    return new NcCustomPickerRenderResult(el)
}, (el, renderResult) => {
    renderResult.element.remove()
})

In your custom component, just emit the submit event with the result as the event's data to pass it back to the Smart Picker. You can also emit the cancel event to abort and go back.