Introduction
Confluence provides creation and collaboration features for different types of content like Spaces, Pages, Attachments, Comments, etc... and all these contents are supported by default within Confluence. It is also possible to create new customised types of content that integrate with Confluence
Just like with the default content types the custom content types behave the same and they integrate tightly with Confluence features such as Search and Navigation, in addition to having the API capabilities of default Confluence content.
For the purpose of explaining custom content entities I'm going to show the implementation we use in Forms for Confluence Connect plugin. In this post I'm not giving much attention on UI elements, but it is more a discussion about how to configure and use custom content entities.
Configuration
To declare custom content we need to add the module to our atlassian-connect.json plugin descriptor. The example below only shows the main parts to configure as to stay in scope of this discussion.
"modules": {
"customContent": [
{
"key": "formconfig",
"name": {
"value": "Form Configurations"
},
"uiSupport": {
...
},
"apiSupport": {
"supportedContainerTypes": [
"space"
],
"supportedChildTypes": [
"ac:com.adaptavist.confluence.formMailNG:formresponse"
]
}
},
{
"key": "formresponse",
"name": {
"value": "Form Responses"
},
"uiSupport": {
...
},
"apiSupport": {
"supportedContainerTypes": [
"ac:com.adaptavist.confluence.formMailNG:formconfig"
],
"supportedChildTypes": [],
"indexing": {
"enabled": false
}
}
}
],
...
}
We started defining two different custom contents: 'Form Configurations' and 'Form Responses'. They both have a unique key and both define a parent-child relationship.
Form Configurations are children of a Space and contain Form Responses.
Form Responses can be contained within a Form Configuration, and that a Form Response is indeed a supported child type of a Form Configuration.
The content type key for custom content is defined in 3 parts:
ac: This is always the same, indicating that this content type is defined in a Connect add-on.
addon-key: The key of the Connect add-on
custom-content: The key of the module which defines the custom content
CRUD actions
Because custom content is directly organised and maintained by Confluence, it can be created, retrieved, updated or deleted using the Confluence REST API. I think that is the most powerful feature of the custom content; the API basically permit us to throw away the database layer and we don't have to spend time to maintain all the different DBMS the clients have.
Following I'm going to show some examples of how the custom content entities are used within Forms for Confluence Connect plugin. The examples will focus on the request itself, nothing is specified on how to handle the response given back from Confluence.
Create
Here's a POST request that is going to store new data in Confluence. In particular we're going to save a Form Configuration with:
- title
- description
- space
- version
- type
The type is going to specify to Confluence that the new entity is our customized content type.
var newFormConfigTitle = AJS.$('#formId').val();
AP.require('request', function (request) {
request({
url: '/rest/api/content',
type: 'POST',
dataType: "json",
contentType: "application/json; charset=utf-8",
data: JSON.stringify({
version: {
number: 1
},
title: newFormConfigTitle,
type: "ac:com.adaptavist.confluence.formMailNG:formconfig",
space: {
key: getUrlParam('space_key')
},
body: {
storage: {
value: AJS.$('#formDescription').val(),
representation: "storage"
}
}
})
});
});
Get
To get all the Form Configurations children of a space, here is the simple request.
AP.require('request', function (request) {
request({
url: '/rest/api/content?type=ac:com.adaptavist.confluence.formMailNG:formconfig' +
'&spaceKey=' + getUrlParam('space_key'),
type: 'GET',
contentType: "application/json; charset=utf-8"
});
});
Delete
And finally how to delete a Form Configuration.
AP.require('request', function (request) {
request({
url: '/rest/api/content/' + formConfigId,
type: 'DELETE',
contentType: 'application/json'
});
});
As we have seen the actions to interact with a custom content entity are very simple. More details related to Confluence API can be found at Confluence Cloud REST API Reference.
Index
Custom content types are indexed as a built-in content type and rendered in quick search and site-wide search. The default value is true, so your custom content type will automatically be indexed if you don't specify a value.
In the following example when we search for the string 'bar' we receive back a list of Form Configurations that match the search condition.
Conclusion
As we have seen custom content well and truly provides a fully-integrated content solution within Confluence. The most interesting benefit I found so far is that we completely dropped the database layer in our architecture. This means there is no time spent to configure a database connection or to fix compatibility problems with one dbms compared to another one. A possible drawback can be that a normal database connection is more flexible and permits easier migrations. I cannot compare the two different solutions now because I didn't need to perfom any migration so far.
- 09 Oct 2018 » A strange bug on AWS Lambda
- 17 Jan 2018 » How to run Karma tests in browsers in Docker
- 07 Dec 2017 » Switching from Javascript to Typescript
- 30 Oct 2017 » Fun with React event handlers
- 17 Jul 2017 » Switching from Groovy to Java
- 24 May 2017 » Useful Git Aliases
- 27 Mar 2017 » Practical Ratpack Promises
- 03 Nov 2016 » Custom Content in Forms for Confluence Connect
- 04 Oct 2016 » Checking user permissions from REST calls
- 30 Sep 2016 » Using the reflection API in Confluence
- 28 Sep 2016 » Creating a custom Confluence Blueprint
- 06 Sep 2016 » ReactJS in Forms for Confluence Connect
- 25 Apr 2016 » Migrating to ES6 in Atlassian Add-ons
- 17 Mar 2016 » All kinds of things I learnt trying to performance test against Fisheye/Crucible
- 24 Dec 2015 » Adaptavist’s Holiday Gift of Atlassian Deployment Automation
- 17 Dec 2015 » Getting a Custom Field value safely
- 07 Dec 2015 » Putting Google Analytics to work with plugins for Confluence
- 02 Dec 2015 » Devoxx Voting, A retrospective
- 25 Nov 2015 » Some things I've learnt about SingleSelect
- 15 Oct 2015 » Using SOY for JIRA actions
- 26 Sep 2015 » Object Reflection in Groovy
- 22 Sep 2015 » Introducing Adaptavist Labs