Comment développer un plugin DocSpace : tutoriel complet étape par étape

21 mai 2025Par Dasha

Dans un monde où la vitesse et la collaboration sont essentielles, les outils de productivité doivent suivre. Les plugins DocSpace offrent un moyen moderne d’améliorer la façon dont les utilisateurs gèrent et interagissent avec les documents. Dans ce guide, nous vous expliquons comment créer votre propre plugin DocSpace à l’aide d’exemples réels tirés de nos échantillons de plugins officiels.

How to develop a DocSpace plugin – complete Step-by-Step Tutorial

À propos de SDK de plugins de ONLYOFFICE DocSpace

Le SDK de plugins de ONLYOFFICE DocSpace est un package npm basé sur des moteurs TypeScript qui fournit des interfaces vous permettant de créer vos propres plugins et de les intégrer dans DocSpace. Le cycle de développement des plugins comprend la planification, le développement, les tests et l’utilisation.

Pour illustrer l’utilisation de notre SDK de plugins, nous avons créé un référentiel avec des exemples de plugins. Nous allons nous plonger dans le processus complet de développement d’un plugin en nous basant sur nos exemples de plugins.

Planification

Définissez l’objectif du plugin, réfléchissez à l’endroit où vous y accéderez et aux services tiers que vous devez connecter.

Étape 1. Installez tous les paquets et programmes nécessaires

Pour installer globalement le paquet npm @onlyoffice/docspace-plugin-sdk, utilisez la commande suivante :

 npm i -g @onlyoffice/docspace-plugin-sdk

Étape 2. Concevez le fonctionnement de votre plugin

Choisissez le service qui vous permet d’ajouter les fonctionnalités nécessaires à votre espace documentaire.

Par exemple, dans nos exemples de plugins, nous utilisons :

  • AssemblyAI pour convertir en texte la parole contenue dans les fichiers audio et vidéo ;
  • ConvertAPI pour convertir des documents texte, des feuilles de calcul, des présentations et des formulaires en PDF ;
  • Draw.io  pour créer, éditer et insérer des diagrammes de qualité professionnelle.

Remarque ! Assurez-vous que la documentation du service est disponible, vérifiez sa licence, la disponibilité des méthodes API, etc. Pour certains services, l’utilisateur doit obtenir une clé API pour commencer à utiliser le plugin.

Réfléchissez à l’endroit où implémenter le plugin, à sa structure, à la manière dont l’utilisateur interagira avec les composants du plugin, etc. En fonction de ces informations, dressez une liste des types de plugins et des éléments requis. Pour plus d’informations, lisez les sections Types de plugins et Éléments de plugins de la documentation du SDK des plugins.

Par exemple, pour le plugin Saisie vocale, nous utilisons les interfaces de plugin suivantes :

  • IPlugin. Nécessaire pour chaque plugin. Il contient la variable PluginStatus du plugin, utilisée pour intégrer le plugin dans le DocSpace.
  • ApiPlugin. Nécessaire car nous mettons en œuvre un service tiers.
  • ISettingsPlugin et ISettings. Utilisés pour ajouter un bloc de paramètres pour la configuration du plugin. Les utilisateurs accèderont à ce bloc à partir de Paramètres -> Intégration -> Plugins pour ajuster les paramètres du plugin.
  • IContextMenuPlugin and IContextMenuItem. Utilisés pour mettre en œuvre une action du menu contextuel. Dans le plugin Saisie vocale, il sera disponible pour les fichiers vidéo et audio, ce qui permettra aux utilisateurs de convertir le contenu en texte.

La liste des interfaces peut être plus longue. Par exemple, dans le plugin draw\.io :

  • IMainButtonPlugin et IMainButtonItem. Utilisés pour mettre en œuvre l’action du bouton principal. Dans le plugin draw\.io, nous utilisons le bouton Action -> Plus d’éléments de menu dans la section Mes documents ou dans la salle sélectionnée pour créer des diagrammes draw\.io.
  • IFilePlugin et IFileItem. Utilisés pour interagir avec les types de fichiers spécifiés. Dans ce cas, avec les fichiers .drawio.

Trouvez la structure du plugin. Tous les fichiers nécessaires sont décrits ici. Tout le reste peut être organisé comme vous le souhaitez. Par exemple, dans le plugin draw\.io, nous avons placé le code pour chaque type de plugin dans des dossiers séparés, alors que dans le plugin Saisie vocale ce code est placé dans le dossier src.

(no title)

Choisissez un nom pour votre plugin et écrivez-en une description.

Développement du plugin

Maintenant que tout le travail de préparation est fait, vous pouvez commencer à développer votre plugin.

Étape 1. Créez un modèle de plugin

Créez un modèle de plugin et configurez ses paramètres qui seront affichés dans les paramètres du plugin DocSpace :

Pour créer un modèle de plugin, ouvrez le terminal et exécutez la commande npx suivante :

 npx create-docspace-plugin

Si la commande npx n’est pas disponible, installez le paquet npm @onlyoffice/docspace-plugin-sdk à l’aide de la commande suivante :

npm i -g @onlyoffice/docspace-plugin-sdk

Configurez le plugin dans le terminal en spécifiant les paramètres dans la boîte de dialogue. Tous les paramètres sont décrits ici.

Pour le plugin Saisie vocale, vous pouvez utiliser les valeurs suivantes :

(no title)

Vous pouvez modifier tous les paramètres spécifiés ultérieurement dans le fichier package.json.

Vous pouvez également créer un plugin dans n’importe quel projet en ajoutant le paquet npm @onlyoffice/docspace-plugin-sdk comme dépendance et en spécifiant tous les champs nécessaires dans le fichier package.json.

Étape 2. Configurez le point d’entrée du plugin

Le index.ts du plugin index.ts sera créé automatiquement dans le dossier src lors de l’étape de création du modèle. Ce fichier est nécessaire.

Ce fichier contient toutes les méthodes de base des types de plugins que vous avez sélectionnés à l’étape précédente. Vous pouvez modifier ce fichier à tout moment.

Si vous créez un plugin vous-même, sans modèle, pour le point d’entrée du plugin, vous pouvez utiliser le code de nos exemples de plugins prêts à l’emploi. Il fonctionnera parfaitement.

Étape 3. Ajoutez les icônes du plugin

Créez le dossier assets dans le dossier racine du plugin et ajoutez-y toutes les icônes du plugin. Le nombre d’icônes et leur taille dépendent des types de plugins que vous mettez en œuvre. Pour le plugin Saisie vocale, nous avons besoin des icônes suivantes :

  • Le type de plugin par défaut nécessite une image de logo. Il est égal au paramètre logo du fichier package.json. Le logo sera affiché dans les paramètres du plugin DocSpace. La taille de l’icône requise est de 48×48 px. Sinon, elle sera compressée à cette taille.(no title)
  • Le plugin du menu contextuel utilise une icône sur le bouton Convertir en texte. La taille requise de l’icône est de 16×16 px. Sinon, elle sera compressée à cette taille.

(no title)

Cette icône peut également être utilisée pour l’icône du bouton principal. Par exemple, dans le plugin draw\.io, la même icône est utilisée pour le menu contextuel et le bouton principal.

(no title)

Le plugin draw\.io utilise également l’icône de fichier spécifique près des fichiers .drawio, qui sont créés avec le type de plugin de fichier. La taille d’icône préférée pour le format de table est de 32×32 px.

(no title)

Étape 4. Configurez les éléments d’interface du plugin

Par exemple, le plugin draw\.io contient deux éléments principaux de l’interface utilisateur – la fenêtre modale et l’éditeur de diagramme. Créez les fichiers de configuration de chaque élément. Pour plus de commodité, vous pouvez placer ces fichiers dans un dossier DrawIO séparé.

Dans le fichier Dialog.ts, configurez les paramètres de la fenêtre modale. Spécifiez le composant IFrame UI qui est utilisé pour intégrer le site web draw\.io dans une fenêtre modale :

  export const frameProps: IFrame = {
    width: "100%",
    height: "100%",
    name: "test-drawio",
    src: "",
  }

Créez le conteneur IBox pour y ajouter l’iframe :

 const body: IBox = {
    widthProp: "100%",
    heightProp: "100%",


    children: [
      {
        component: Components.iFrame,
        props: frameProps,
      },
    ],
  }

Configurez les propriétés de la fenêtre modale :

 export const drawIoModalDialogProps: IModalDialog = {
    dialogHeader: "",
    dialogBody: body,
    displayType: ModalDisplayType.modal,
    fullScreen: true,
    onClose: () => {
      const message: IMessage = {
        actions: [Actions.closeModal],
      }
      return message
    },
    onLoad: async () => {
      return {
        newDialogHeader: drawIoModalDialogProps.dialogHeader || "",
        newDialogBody: drawIoModalDialogProps.dialogBody,
      }
    },
    autoMaxHeight: true,
    autoMaxWidth: true,

Dans le fichier Editor.ts, configurez l’éditeur de diagramme. Créez la fonction DiagramEditor avec les paramètres suivants :

Paramètre Type Exemple Description
ui string “default” Définit le thème de l’interface utilisateur de l’éditeur.
dark string “auto” Définit le thème sombre de l’éditeur.
off boolean false Spécifie si le mode hors ligne est actif ou non.
lib boolean false Spécifie si les bibliothèques sont activées ou non.
lang string “auto” Définit la langue de l’éditeur.
url string `https://embed.diagrams.net` Définit l’URL de l’éditeur.
showSaveButton boolean true Spécifie si le bouton **Save** sera affiché dans l’éditeur.

Spécifiez ensuite des méthodes pour travailler avec des diagrammes :

Méthode Description
startEditing Démarre l’éditeur avec les données fournies.
getData Renvoie les données du diagramme.
getTitle Renvoie le titre du diagramme.
getFormat Renvoie le format du diagramme.
getFrameId Renvoie l’identifiant du cadre de l’éditeur.
getFrameUrl Renvoie l’URL de l’iframe.
handleMessage Traite le message donné.
initializeEditor Affiche le message de *chargement* à l’éditeur.
save Enregistre les données fournies.

Le code complet de *DiagramEditor* est disponible ici.

Étape 5. Créez des types de plugins

Maintenant que le plugin par défaut est prêt, vous pouvez commencer à coder d’autres types de plugins.

Chaque type de plugin possède des éléments de plugin spécifiques. Définissez l’élément du menu contextuel qui s’affichera lorsque vous ferez un clic droit sur les fichiers audio ou vidéo :

export const contextMenuItem: IContextMenuItem = {
  key: "speech-to-text-context-menu-item",
  label: "Convert to text",
  icon: "speech-to-text-16.png",
  onClick: assemblyAI.speechToText,
  fileType: [FilesType.video],
  withActiveItem: true,
}

(no title)

Vous pouvez ajouter d’autres types de plugins. Par exemple, le plugin draw\.io est accessible depuis le menu du bouton principal, nous devons donc spécifier l’élément du bouton principal :

const mainButtonItem: IMainButtonItem = {
  key: "draw-io-main-button-item",
  label: "Draw.io",
  icon: "drawio.png",
  onClick: (id: number) => {
    drawIo.setCurrentFolderId(id)


    const message: IMessage = {
      actions: [Actions.showCreateDialogModal],
      createDialogProps: {
        title: "Create diagram",
        startValue: "New diagram",
        visible: true,
        isCreateDialog: true,
        extension: ".drawio",
        onSave: async (e: any, value: string) => {
          await drawIo.createNewFile(value)
        },
        onCancel: (e: any) => {
          drawIo.setCurrentFolderId(null)
        },
        onClose: (e: any) => {
          drawIo.setCurrentFolderId(null)
        },
      },
    }
    return message
  },
  // items: [createItem],
}

Lorsque vous cliquez sur le bouton principal, une fenêtre modale apparaît dans laquelle vous pouvez saisir le nom du diagramme et ouvrir un fichier .drawio vide.

(no title)

Pour le plugin draw\.io, vous devez également configurer le type de plugin de fichier qui fonctionne lorsque l’utilisateur ouvre le fichier .drawio spécifique :

Définissez l’élément de fichier qui est représenté sous la forme d’un fichier portant l’extension spécifique (.drawio) et l’icône :

 export const drawIoItem: IFileItem = {
     extension: ".drawio",
     fileTypeName: "Diagram",
     fileRowIcon: "drawio-32.svg",
     fileTileIcon: "drawio-32.svg",
     devices: [Devices.desktop],
     onClick,
   }

Définissez l’événement onClick qui exécutera la méthode editDiagram chaque fois que le fichier .drawio sera ouvert :

  const onClick = async (item: File) => {
     return await drawIo.editDiagram(item.id)
   }

(no title)

Étape 6. Créez le type de plugin de paramétrage

Configurez le type de plugin des paramètres pour fournir aux utilisateurs les paramètres de l’administrateur.

Créez un conteneur dans lequel les paramètres du plugin seront placés :

 const descriptionText: TextGroup = {
     component: Components.text,
     props: {
       text: "To generate API token visit https://www.assemblyai.com",
       color: "#A3A9AE",
       fontSize: "12px",
       fontWeight: 400,
       lineHeight: "16px",
     },
   }
   
   const descGroup: BoxGroup = {
     component: Components.box,
     props: {children: [descriptionText]},
   }
   
   const parentBox: IBox = {
     displayProp: "flex",
     flexDirection: "column",
     // marginProp: "16px 0 0 0",
     children: [tokenGroup, descGroup],
   }

Dans la description des paramètres, indiquez qu’il est nécessaire de générer un jeton API pour pouvoir travailler avec le plugin.

Configurez les paramètres de l’administrateur avec l’objet ISettings :

const adminSettings: ISettings = {
     settings: parentBox,
     saveButton: userButtonComponent,
     onLoad: async () => {
       assemblyAI.fetchAPIToken()
   
       tokenInput.value = assemblyAI.apiToken
   
       if (!assemblyAI.apiToken) {
         return {
           settings: parentBox,
         }
       }


       plugin.setAdminPluginSettings(adminSettings)
   
       return {settings: parentBox}
     }

Spécifiez l’événement onLoad qui définit les paramètres du plugin qui seront affichés lorsque le bloc settins est chargé.

Chaque élément de réglage est déterminé dans des fichiers séparés (boutons, jetons).

(no title)

Étape 7. Créez le fichier de code principal du plugin

Créez un fichier dans le dossier src avec le code principal du plugin. Ce fichier est obligatoire. Reportez-vous à la documentation d’un service tiers pour écrire ce code.

Voyons comment le fichier AssemblyAI.ts est organisé en détail :

Définissez la classe AssemblyAI avec toutes les variables et méthodes nécessaires :

  • Variables et leur description :

apiURL

Définit l’URL de l’API.

apiURL = ""

currentFileId

Définit l’ID du fichier actuel.

const currentFileId: numbernull | number = null

apiToken

Définit le jeton de l’API.

 apiToken = ""
  • Méthodes et leur description :

createAPIUrl

Crée l’URL de l’API.

 createAPIUrl = () => {
     const api = plugin.getAPI()


     this.apiURL = api.origin.replace(/\/+$/, "")


     const params = [api.proxy, api.prefix]


     if (this.apiURL) {
       for (const part of params) {
         if (!part) {
           continue
         }
         const newPart = part.trim().replace(/^\/+/, "")
         if (newPart) {
           if (this.apiURL.length !== 0 && this.apiURL[this.apiURL.length - 1] === "/") {
             this.apiURL += newPart
           } else {
             this.apiURL += `/${newPart}`
           }
         }
       }
     }
   }

setAPIUrl

Définit l’URL de l’API.

 setAPIUrl = (url: string) => {
     this.apiURL = url
   }

getAPIUrl

Définit l’URL de l’API.

 getAPIUrl = () => {
     return this.apiURL
   }

setAPIToken

Définit le jeton API.

   setAPIToken = (apiToken: string) => {
     this.apiToken = apiToken
   }

getAPIToken

Renvoie le code API.

   getAPIToken = () => {
     return this.apiToken
   }

fetchAPIToken

Récupère le jeton de l’API.

   fetchAPIToken = async () => {
     const apiToken = localStorage.getItem("speech-to-text-api-token")
  
     if (!apiToken) {
       return
     }


     this.setAPIToken(apiToken)
     plugin.updateStatus(PluginStatus.active)
   }

saveAPIToken

Enregistre le jeton API.

   saveAPIToken = (apiToken: string) => {
     localStorage.setItem("speech-to-text-api-token", apiToken)
  
     let status
     if (apiToken) {
       status = PluginStatus.active
     } else {
       status = PluginStatus.hide
     }
     plugin.updateStatus(status)
   }

setCurrentFileId

Définit l’ID du fichier en cours.

 setCurrentFileId = (id: number | null) => {
     this.currentFileId = id
   }

uploadFile

Charge un fichier qui sera transcrit.

uploadFile = async (apiToken: string, path: string, data: Blob) => {
     console.log(`Uploading file: ${path}`)
  
     const url = "https://api.assemblyai.com/v2/upload"
  
     try {
       const response = await fetch(url, {
         method: "POST",
         body: data,
         headers: {
           "Content-Type": "application/octet-stream",
           "Authorization": apiToken,
         },
       })
  
       if (response.status === 200) {
         const responseData = await response.json()
         return responseData["upload_url"]
       }
       console.error(`Error: ${response.status} - ${response.statusText}`)
       return null
     } catch (error) {
       console.error(`Error: ${error}`)
       return null
     }
   }

transcribeAudio

Transcrit le fichier audio.

transcribeAudio = async (apiToken: string, audioUrl: string) => {
     console.log("Transcribing audio... This might take a moment.")
  
     const headers = {
       "authorization": apiToken,
       "content-type": "application/json",
     }
     const response = await fetch("https://api.assemblyai.com/v2/transcript", {
       method: "POST",
       body: JSON.stringify({audioUrl}),
       headers,
     })
  
     const responseData = await response.json()
     const transcriptId = responseData.id
  
     const pollingEndpoint = `https://api.assemblyai.com/v2/transcript/${transcriptId}`
  
     while (true) {
       const pollingResponse = await fetch(pollingEndpoint, {headers})
       const transcriptionResult = await pollingResponse.json()
  
       if (transcriptionResult.status === "completed") {
         return transcriptionResult
       } else if (transcriptionResult.status === "error") {
         throw new Error(`Transcription failed: ${transcriptionResult.error}`)
       } else {
         await new Promise((resolve) => {
           setTimeout(resolve, 3000)
         })
       }
     }
   }

speechToText

Implémente la fonctionnalité du plugin.

speechToText = async (id: number) => {
     if (!this.apiToken) {
       return
     }


     this.setCurrentFileId(null)
  
     if (!this.apiURL) {
       this.createAPIUrl()
     }


     const response = await fetch(`${this.apiURL}/files/file/${id}`)
     const data = await response.json()
     const {viewUrl, title, folderId, fileExst} = data.response
  
     const file = await fetch(viewUrl)
  
     const fileBlob = await file.blob()
  
     const uploadUrl = await this.uploadFile(this.apiToken, viewUrl, fileBlob)
  
     const transcript = await this.transcribeAudio(this.apiToken, uploadUrl)
  
     const blob = new Blob([transcript.text], {
       type: "text/plain;charset=UTF-8",
     })
  
     const newFile = new File([blob], "blob", {
       type: "",
       lastModified: Date.now(),
     })
  
     const formData = new FormData()
     formData.append("file", newFile)
  
     const newTitle = `${title.replaceAll(fileExst, "")} text`
  
     try {
       const sessionRes = await fetch(
         `${this.apiURL}/files/${folderId}/upload/create_session`,
         {
           method: "POST",
           headers: {
             "Content-Type": "application/json;charset=utf-8",
           },
           body: JSON.stringify({
             createOn: new Date(),
             fileName: `${newTitle}.txt`,
             fileSize: newFile.size,
             relativePath: "",
           }),
         },
       )
       const response = await sessionRes.json()
       const sessionData = response.response.data
  
       const data = await fetch(`${sessionData.location}`, {
         method: "POST",
         body: formData,
       })


       const jsonData = await data.json()
       const {id: fileId} = jsonData.data
  
       return fileId
     } catch (e) {
       console.log(e)
     }


     return {
       actions: [Actions.showToast],
       toastProps: [{type: ToastType.success, title: ""}],
     } as IMessage
   }

Déclarez l’instance de la classe AssemblyAI :

const assemblyAI = new AssemblyAI()

Exportez l’instance de plugin créée :

export default assemblyAI

Tests

Pour vérifier la fonctionnalité du plugin et corriger les éventuels bogues, testez le plugin :

  • Construisez le plugin préparé en suivant les instructions ici

Le dossier dist sera créé dans le dossier racine du plugin et l’archive du plugin y sera placée. Cette archive est le plugin complet qui peut être téléchargé sur DocSpace.

  • Téléchargez votre plugin sur DocSpace et testez minutieusement son apparence et sa fonctionnalité.

Veuillez noter que vous ne pouvez télécharger vos propres plugins que dans la version serveur de DocSpace.

Si des bogues apparaissent, corrigez le code source de votre plugin et répétez la procédure de construction et de test.

Maintenant que votre plugin est testé et fonctionne correctement, vous pouvez l’ajouter à la version serveur de DocSpace et commencer à l’utiliser.

Le plugin DocSpace offre une approche puissante et conviviale de la gestion des documents et de la collaboration. En s’intégrant aux plateformes préférées des utilisateurs, il élimine les obstacles courants et améliore la productivité à travers différents flux de travail. Si vous avez des questions sur les plugins DocSpace, n’hésitez pas à les poser à nos développeurs sur le forum ONLYOFFICE (inscription requise). Vous pouvez également demander une fonctionnalité ou signaler un bug en postant un problème sur GitHub ou en partageant vos plugins DocSpace avec nous et d’autres utilisateurs. Nous vous souhaitons bonne chance dans vos projets exploratoires !

Créez votre compte ONLYOFFICE gratuit

Affichez, modifiez et coéditez des documents texte, feuilles de calcul, diapositives, formulaires et fichiers PDF en ligne.