Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Customizing Confluence with Forge

Customizing Confluence with Forge

In this 90-minute training session, we will explore how to customize Confluence Cloud with Forge. Forge is a platform that allows developers to create and deploy apps and integrations that extend the functionality of Confluence. We will cover the basics of using Forge, including how to set up your environment, create a Forge app, and deploy it to Confluence Cloud. Throughout the session, we will also engage in hands-on exercises to reinforce your learning and give you practical experience with customizing Confluence Cloud with Forge.

Prerequisites

To attend this training session, participants should meet the following prerequisites:

Have a working knowledge of Confluence Cloud and basic web development concepts

Set up an Atlassian cloud developer site

Have set up the necessary tools, including Forge CLI and Docker, which requires a fully supported LTS release of Node.js installed (versions 16.x or 18.x)

Be able to follow instructions and complete tasks independently

To ensure that participants are adequately prepared for the training, they should complete the "Getting started: creating a hello world app in Forge" tutorial and set up their environment according to the instructions provided for their respective platform (Apple macOS, Linux, or Windows 10).

If you need help getting set up please create a topic on the Developer Community.

Ralph Whitbeck

June 27, 2023
Tweet

More Decks by Ralph Whitbeck

Other Decks in Technology

Transcript

  1. • Reference Hands-On Code Repo 
 https://bitbucket.org/atlassian/dd23-forge-confluence • Slides (also

    on Readme in above repo) 
 https://speakerdeck.com/rwhitbeck/customizing- confluence-with-forge REPO & SLIDES
  2. Agenda What is Forge? Let’s Build an App API Calls

    Forge Storage Extending Confluence - Macro
  3. Reasons to build a Forge App Marketplace Opportunity Monetize your

    idea by selling it on the Atlassian Marketplace Customized User Experience Tailor the user experience to meet specific needs and preferences Process Automation Automate repetitive tasks, streamline workflows, and eliminate manual processes Integrate External Tools Enabling seamless data exchange and collaboration between different tools Solve Business Needs Address specific pain points or requirements within your organization
  4. Agenda What is Forge? Let’s Build an App API Calls

    Forge Storage Extending Confluence - Macro
  5. • Run forge create in a terminal window • Create

    a new app called confluence-page-ring • Select the template category UI kit > confluence-macro • cd into the new directory that forge create created • Run forge deploy • Run forge install Select Confluence and give it your developer instance • Create a page on the developer instance and type /confluence page ring 
 and make sure you can add the macro onto your page • Download / Clone code repo: https://bitbucket.org/atlassian/dd23-forge- confluence HANDS-ON - SET-UP OUR PROJECT
  6. Agenda What is Forge? Let’s Build an App API Calls

    Forge Storage Extending Confluence - Macro
  7. Call an Confluence API from Forge 1. Request info on

    the current page 2. Get the title of the current page 3. Display the title on the page
  8. Things we’ll need • Product Context and Update State •

    @forge/ui • Installed with UI Kit in the template 
 • Product Authentication Fetch Package • @forge/api • Needs to be installed
 • Confluence API v2 - GET Page by ID /pages/{id}
  9. useState • adds and updates local state to a component

    import ForgeUI, { useState, Button, Macro, render } from '@forge/ui'; const App = () => { const [count, setCount] = useState(0); return ( <Button text={`Count is ${count}`} onClick={() => { setCount(count + 1); }} /> ); }; export const run = render(<Macro app={<App />} />); Docs: https://developer.atlassian.com/platform/forge/ui-kit-hooks-reference/#usestate
  10. useState • adds and updates local state to a component

    import ForgeUI, { useState, Button, Macro, render } from '@forge/ui'; const App = () => { const [count, setCount] = useState(0); return ( <Button text={`Count is ${count}`} onClick={() => { setCount(count + 1); }} /> ); }; export const run = render(<Macro app={<App />} />); Docs: https://developer.atlassian.com/platform/forge/ui-kit-hooks-reference/#usestate
  11. useProductContext • reads the context in which the component is

    currently running const context = useProductContext(); Docs: https://developer.atlassian.com/platform/forge/ui-kit-hooks-reference/#usestate
  12. import ForgeUI, { render, Fragment, Macro, Text, useProductContext, useState }

    from "@forge/ui"; Docs: https://developer.atlassian.com/platform/forge/ui-kit-hooks-reference/ 
 https://developer.atlassian.com/platform/forge/ui-kit/#hooks
  13. // Get info of the current page, including Content ID

    const context = useProductContext(); console.log("context: " + JSON.stringify(context)); console.log("context.contentId: " + context.contentId); Docs: https://developer.atlassian.com/platform/forge/ui-kit-hooks-reference/
  14. const context = useProductContext(); console.log("context: " + JSON.stringify(context)); { "accountId":

    {accountId}, "accountType": "licensed", "cloudId": {cloudId}, "contentId": "393217", "localId": {localId}, "spaceKey": "~557057729b34f7b3a34dda9883b1b9ed7dc794", "installContext": “{installContext}”, "isConfig": false, "extensionContext": { "type": "macro", "isEditing": false, "references": [] }, "moduleKey": "confluence-page-ring-macro-hello-world" } console.log("context.contentId: " + context.contentId); 393217 Docs: https://developer.atlassian.com/platform/forge/ui-kit-hooks-reference/
  15. const context = useProductContext(); console.log("context: " + JSON.stringify(context)); { "accountId":

    {accountId}, "accountType": "licensed", "cloudId": {cloudId}, "contentId": "393217", "localId": {localId}, "spaceKey": "~557057729b34f7b3a34dda9883b1b9ed7dc794", "installContext": “{installContext}”, "isConfig": false, "extensionContext": { "type": "macro", "isEditing": false, "references": [] }, "moduleKey": "confluence-page-ring-macro-hello-world" } console.log("context.contentId: " + context.contentId); 393217 Docs: https://developer.atlassian.com/platform/forge/ui-kit-hooks-reference/
  16. @forge/api A partial implementation of node-fetch, which fetches data from

    an HTTP server. $ npm install @forge/api Handles Product Authentication for us
  17. import api, { route } from "@forge/api"; const response =

    await api.asUser() .requestConfluence(route`/wiki/api/v2/pages/{id}`, { headers: { 'Accept': 'application/json' } }); console.log(`Response: ${response.status}`); console.log(await response.json()); Docs: https://developer.atlassian.com/cloud/confluence/rest/v2/api-group-page/#api-pages-id-get
  18. import api, { route } from "@forge/api"; const response =

    await api.asUser() .requestConfluence(route`/wiki/api/v2/pages/{id}`, { headers: { 'Accept': 'application/json' } }); console.log(`Response: ${response.status}`); console.log(await response.json()); Docs: https://developer.atlassian.com/cloud/confluence/rest/v2/api-group-page/#api-pages-id-get
  19. import api, { route } from "@forge/api"; const response =

    await api.asUser() .requestConfluence(route`/wiki/api/v2/pages/{id}`, { headers: { 'Accept': 'application/json' } }); console.log(`Response: ${response.status}`); console.log(await response.json()); Docs: https://developer.atlassian.com/cloud/confluence/rest/v2/api-group-page/#api-pages-id-get
  20. const response = await api.asUser() .requestConfluence(route`/wiki/api/v2/pages/{id}`, { headers: { 'Accept':

    'application/json' } }); Docs: https://developer.atlassian.com/platform/forge/runtime-reference/product-fetch-api/#contextual-methods Contextual Method • api.asUser() - call an Atlassian API as the user of the app • api.asApp() - Call an Atlassian API as the app, using the app user. The call 
 will work regardless of who used the app.
  21. const response = await api.asUser() .requestConfluence(route`/wiki/api/v2/pages/{id}`, { headers: { 'Accept':

    'application/json' } }); Docs: https://developer.atlassian.com/platform/forge/runtime-reference/product-fetch-api/#requestconfluence https://www.npmjs.com/package/node-fetch#options requestConfluence • Makes a request to the Confluence REST API • Takes a path built using the route tagged template function • Second argument is the options based on node-fetch library
  22. const response = await api.asUser() .requestConfluence(route`/wiki/api/v2/pages/${contentId}`, { headers: { 'Accept':

    'application/json' } }); Docs: https://developer.atlassian.com/platform/forge/runtime-reference/product-fetch-api/#route 
 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent • Tagged Template Function • Constructs the path that's passed to the product fetch APIs • Runs encodeURIComponent on each interpolated parameter in the template string • protection against security vulnerabilities, such as path traversal and query string injection route
  23. COLLABORATIVE HANDS-ON • Run npm install @forge/api in the newly

    created project • Get the product context and save the contentID to a variable • Create a function called fetchPage that takes in the context.contentID 
 and returns the page title of the current page. • Call the new fetchPage function using useState • Update the manifest.yml and add the scope needed
  24. Agenda What is Forge? Let’s Build an App API Calls

    Forge Storage Extending Confluence - Macro
  25. FORGE STORAGE • Stores JSON value with a specified key

    • Data stored isn’t shared between Forge apps on the same or different sites • Subject to quotas and limits https://developer.atlassian.com/platform/forge/platform- quotas-and-limits/ • Your app needs the storage:app scope • Import the storage function from the @forge/api package
 import { storage } from “@forge/api"; • Types of values to be saved: arrays, boolean, numbers, objects, strings
  26. FORGE STORAGE - SETTING A VALUE import { storage }

    from "@forge/api"; storage.set(key, value); var arrPageRing = []; arrPageRing.push(3569276); storage.set(“page-ring", arrPageRing); Docs: https://developer.atlassian.com/platform/forge/runtime-reference/storage-api/
  27. FORGE STORAGE - GETTING A VALUE import { storage }

    from "@forge/api"; // storage.get returns a promise var fsData = await storage.get(key); var fsData = await storage.get("page-ring"); console.log(fsData); // [3569276] Docs: https://developer.atlassian.com/platform/forge/runtime-reference/storage-api/
  28. FORGE STORAGE - DELETING A VALUE import { storage }

    from "@forge/api"; storage.delete(key); storage.delete("page-ring"); Docs: https://developer.atlassian.com/platform/forge/runtime-reference/storage-api/
  29. COLABORATIVE HANDS-ON 1. Test if the page is already stored.

    2. Save the contentID into an array and into Forge Storage 3. Get the Adjacent page in the direction desired (next or previous)
  30. Agenda What is Forge? Let’s Build an App API Calls

    Forge Storage Extending Confluence - Macro
  31. CONFLUENCE MODULES • Content action • Content Byline item •

    Context menu • Custom Content • Global Page • Global Settings • Homepage Feed • Space page • Space settings • Macro Docs: https://developer.atlassian.com/platform/forge/manifest-reference/modules/index-confluence/
  32. import ForgeUI, { render, Fragment, Macro, Text } from "@forge/ui";

    const App = () => { return ( <Fragment> <Text>Hello World!</Text> </Fragment> ); }; export const run = render( <Macro app={<App />} /> ); Docs: https://developer.atlassian.com/platform/forge/ui-kit-components/confluence/macro/
  33. COMMON UI KIT COMPONENTS - FRAGMENT return ( <Fragment> </Fragment>

    ); Docs: https://developer.atlassian.com/platform/forge/ui-kit-components/fragment/
  34. COMMON UI KIT COMPONENT - TABLE return ( <Fragment> <Table>

    <Row> <Cell> <Text> Hello World! </Text> </Text> </Row> </Table> </Fragment> ); Docs: https://developer.atlassian.com/platform/forge/ui-kit-components/table/
  35. COMMON UI KIT COMPONENT - TEXT return ( <Fragment> <Table>

    <Row> <Cell> <Text> <Link appearance="link" href={"/wiki" + Page._links.webui}>{Page.title}</Link> </Text> </Cell> </Row> </Table> </Fragment> ); Docs: https://developer.atlassian.com/platform/forge/ui-kit-components/text/
  36. COMMON UI KIT COMPONENTS - LINK return ( <Fragment> <Table>

    <Row> <Cell> <Text> <Link appearance="link" href={"/wiki" + Page._links.webui}>{Page.title}</Link> </Text> </Cell> </Row> </Table> </Fragment> ); Docs: https://developer.atlassian.com/platform/forge/ui-kit-components/text/#link
  37. COMMON UI KIT COMPONENTS import ForgeUI, { render, Fragment, Macro,

    Table, Row, Cell, Text, Link} from "@forge/ui";