Naar inhoud
lightbulb Welkom op de nieuwe kennisbank | We hebben de docs volledig vernieuwd met meer dan 160 features. Bekijk wat nieuw isarrow_forward

Google Workspace Add-on bouwen

Bouw een Google Workspace Add-on die werkt in Gmail, Drive, Docs, Sheets en Calendar met een Cards-gebaseerde UI, contextuele triggers en deployment.

Workspace Add-on of Editor Add-on

Er zijn twee soorten add-ons in het Google-ecosysteem, en het is belangrijk dat je vooraf het juiste type kiest:

  • Editor Add-ons: draaien in Sheets, Docs, Slides en Forms via het menu Extensies. Ze zijn alleen beschikbaar als de gebruiker het betreffende bestand open heeft, en kunnen een eigen zijbalk tonen met HtmlService.
  • Workspace Add-ons: verschijnen in de zijbalk van Gmail, Drive, Calendar, Docs, Sheets, Slides en Meet. Ze gebruiken het Cards-framework en werken over meerdere apps tegelijk.

Dit artikel behandelt Workspace Add-ons op basis van het moderne Cards-framework. Dat is de aanbevolen aanpak als je dezelfde add-on in meerdere Google-apps wilt aanbieden.

Manifest configureren

Het appsscript.json manifest definieert de triggers, het runtime en de OAuth-scopes. Schakel in de Apps Script-editor eerst Project Settings en de optie Show appsscript.json manifest file in editor in.

{
  "timeZone": "Europe/Amsterdam",
  "dependencies": {},
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "addOns": {
    "common": {
      "name": "Mijn Add-on",
      "logoUrl": "https://example.com/icon.png",
      "homepageTrigger": {
        "runFunction": "buildHomepageCard"
      }
    },
    "gmail": {
      "contextualTriggers": [{
        "unconditional": {},
        "onTriggerFunction": "buildGmailContextCard"
      }]
    }
  },
  "oauthScopes": [
    "https://www.googleapis.com/auth/gmail.readonly",
    "https://www.googleapis.com/auth/script.locale"
  ]
}

De homepageTrigger onder common bouwt de niet-contextuele beginpagina van je add-on, die in elke ondersteunde host verschijnt. De contextualTriggers onder gmail reageren op een geopend bericht. Een contextuele Gmail-trigger ondersteunt op dit moment alleen het criterium unconditional, wat betekent dat hij voor elk geopend bericht afgaat.

warning

Vraag alleen de scopes die je nodig hebt

Beperk de OAuth-scopes tot het minimum dat je add-on echt gebruikt. Brede scopes zoals gmail.modify of volledige Drive-toegang vertragen de Marketplace-review en schrikken gebruikers af bij het goedkeuringsscherm. Begin met readonly-varianten en breid alleen uit wanneer een functie er echt om vraagt.

Homepage card bouwen

De homepage-functie geeft een Card-object terug dat in de zijbalk verschijnt:

function buildHomepageCard() {
  const card = CardService.newCardBuilder();
  card.setName('homepage');

  const header = CardService.newCardHeader()
    .setTitle('Mijn Workspace Add-on')
    .setSubtitle('Versie 1.0')
    .setImageUrl('https://example.com/icon.png');
  card.setHeader(header);

  const sectie = CardService.newCardSection()
    .setHeader('Snelle acties');

  const knop1 = CardService.newTextButton()
    .setText('Rapport genereren')
    .setOnClickAction(
      CardService.newAction().setFunctionName('genereerRapport')
    );

  sectie.addWidget(knop1);
  card.addSection(sectie);

  return card.build();
}

Contextuele Gmail-card

Wanneer een gebruiker een e-mail opent, vuurt de contextuele trigger. Het event-object bevat het bericht-ID:

function buildGmailContextCard(e) {
  const berichtId = e.gmail.messageId;
  const bericht = GmailApp.getMessageById(berichtId);

  const card = CardService.newCardBuilder();

  const sectie = CardService.newCardSection()
    .setHeader('E-mailinformatie');

  sectie.addWidget(
    CardService.newKeyValue()
      .setTopLabel('Afzender')
      .setContent(bericht.getFrom())
  );

  sectie.addWidget(
    CardService.newKeyValue()
      .setTopLabel('Datum')
      .setContent(bericht.getDate().toLocaleDateString('nl-NL'))
  );

  const opslaanKnop = CardService.newTextButton()
    .setText('Opslaan in Sheets')
    .setOnClickAction(
      CardService.newAction()
        .setFunctionName('slaEmailDetailsOp')
        .setParameters({berichtId})
    );

  sectie.addWidget(CardService.newButtonSet().addButton(opslaanKnop));
  card.addSection(sectie);

  return card.build();
}
info

Bericht ophalen vanuit de trigger

Contextuele triggers in Gmail ontvangen het bericht-ID in e.gmail.messageId. Haal het volledige bericht op met GmailApp.getMessageById(id). Let op: hiervoor heb je de scope gmail.addons.current.message.readonly of gmail.readonly nodig, anders krijg je een autorisatiefout.

Action-functies implementeren

Een action-functie wordt aangeroepen wanneer de gebruiker op een knop klikt. Ze geeft een ActionResponse terug, bijvoorbeeld om een notificatie te tonen:

function slaEmailDetailsOp(e) {
  const berichtId = e.parameters.berichtId;
  const bericht = GmailApp.getMessageById(berichtId);

  const blad = SpreadsheetApp.openById(
    PropertiesService.getScriptProperties().getProperty('SHEET_ID')
  ).getActiveSheet();

  blad.appendRow([
    new Date(),
    bericht.getFrom(),
    bericht.getSubject(),
    bericht.getDate(),
  ]);

  return CardService.newActionResponseBuilder()
    .setNotification(
      CardService.newNotification().setText('E-mail opgeslagen in Sheets')
    )
    .build();
}
lightbulb

Bewaar configuratie in script properties

Hardcode geen sheet-ID's, API-sleutels of webhook-URL's in je code. Gebruik PropertiesService.getScriptProperties() voor instellingen die per deployment verschillen, en getUserProperties() voor gebruikersspecifieke voorkeuren. Zo kun je dezelfde code veilig delen en deployen zonder gevoelige waarden in de broncode.

Formulier-invoer in add-ons

Cards ondersteunen invoervelden en keuzelijsten. De ingevulde waarden komen binnen via e.formInput:

function buildFormCard() {
  const sectie = CardService.newCardSection();

  const naamInvoer = CardService.newTextInput()
    .setFieldName('naam')
    .setTitle('Naam')
    .setHint('Volledige naam');

  const categorieDropdown = CardService.newSelectionInput()
    .setType(CardService.SelectionInputType.DROPDOWN)
    .setFieldName('categorie')
    .setTitle('Categorie')
    .addItem('Verkoop', 'verkoop', false)
    .addItem('Support', 'support', true)
    .addItem('Administratie', 'admin', false);

  const verstuurKnop = CardService.newTextButton()
    .setText('Versturen')
    .setOnClickAction(
      CardService.newAction().setFunctionName('verwerkFormulier')
    );

  sectie.addWidget(naamInvoer)
    .addWidget(categorieDropdown)
    .addWidget(CardService.newButtonSet().addButton(verstuurKnop));

  return CardService.newCardBuilder()
    .addSection(sectie)
    .build();
}

function verwerkFormulier(e) {
  const naam = e.formInput.naam;
  const categorie = e.formInput.categorie;

  Logger.log(`Formulier: ${naam}, ${categorie}`);

  return CardService.newActionResponseBuilder()
    .setNotification(CardService.newNotification().setText(`Bedankt, ${naam}!`))
    .build();
}

Testen en deployen

Tijdens de ontwikkeling test je je add-on met een test-deployment, zodat je niet bij elke wijziging opnieuw hoeft te publiceren.

Add-on testen en intern uitrollen

  1. Open in de Apps Script-editor Deploy en kies Test deployments.
  2. Klik op Install om de testversie in je eigen account te activeren.
  3. Open de bijbehorende host, bijvoorbeeld Gmail, en controleer de zijbalk.
  4. Pas je code aan en herlaad de host: de testversie gebruikt automatisch je laatste opgeslagen code.
  5. Tevreden? Kies Deploy en New deployment met het type Add-on.
  6. Deel de add-on intern via het Script ID of via de Google Workspace Marketplace SDK voor je domein.

Voor publieke verspreiding heb je een Google Cloud-project nodig met een geconfigureerd OAuth-consentscherm, een ingevulde Google Workspace Marketplace SDK en een review door Google. Houd er rekening mee dat de review enkele dagen tot weken kan duren, vooral bij gevoelige of restricted scopes.

Hoe test ik een add-on voordat ik hem publiceer?

Gebruik Test deployments in de Apps Script-editor. Daarmee installeer je een testversie in je eigen account die altijd je laatst opgeslagen code draait, zodat je wijzigingen direct in de echte host ziet zonder te publiceren.

Kan een add-on bij de Drive-bestanden van de gebruiker?

Ja, mits je de juiste OAuth-scopes in het manifest opneemt, bijvoorbeeld drive.file voor alleen bestanden die de gebruiker opent of drive.readonly voor leestoegang. De gebruiker keurt deze scopes goed bij het eerste gebruik.

Hoe voeg ik mijn add-on toe aan de Workspace Marketplace?

Je hebt een Google Cloud-project nodig, een geconfigureerd OAuth-consentscherm en de Google Workspace Marketplace SDK. Daarin vul je de listing, screenshots en scopes in en dien je de add-on ter review in bij Google.

Wat zijn de beperkingen van de Cards-UI?

De Cards-UI ondersteunt een vaste set widgets, zoals tekst, knoppen, afbeeldingen, keyvalue-rijen en formuliervelden. Voor vrij vormgegeven of interactieve HTML gebruik je een Editor Add-on met HtmlService in plaats van het Cards-framework.

Werkt dezelfde add-on in meerdere apps tegelijk?

Ja. Je definieert gedeelde logica onder addOns.common en voegt per host, zoals gmail, drive of calendar, specifieke triggers toe. Eén deployment levert zo één add-on die in al die apps verschijnt.

Hoe sla ik gevoelige instellingen veilig op?

Gebruik PropertiesService. Met getScriptProperties() bewaar je deployment-brede waarden zoals een sheet-ID en met getUserProperties() gebruikersspecifieke voorkeuren. Zet nooit sleutels of ID's rechtstreeks in je broncode.

Workspace Add-ons integreren je eigen functionaliteit naadloos in de dagelijkse Google-tools van je team. Met het Cards-framework bouw je consistente UI die in Gmail, Drive, Docs, Sheets en Calendar werkt, terwijl je met scopes, script properties en test-deployments grip houdt op veiligheid en uitrol.