Applicable: | This tutorial applies to Confluence 7.5 and later |
Level of experience: | This is an intermediate tutorial. You should have completed at least one beginner tutorial before working through this tutorial. See the list of developer tutorials. |
This tutorial will guide you through the process of adding a new screen in Space Tools. This is useful if you need to provide the ability for your app to be configured on a space-by-space basis.
There are two ways you can do this:
We'll describe both methods below.
You can use any renderer (Velocity, Soy, etc) as long as you provide the correct decorator name in the HTML output.
To complete this tutorial, you should:
If you want to skip ahead or check your work when you finish, you can find the plugin source code on Atlassian Bitbucket. Alternatively, you can download the source as a ZIP archive. To clone the repository, issue the following command:
1 2git clone https://bitbucket.org/atlassian_tutorial/space-plugin-example.git
First we need to add a web-item for Space Tools in atlassian-plugin.xml
. If the screen should only be visible to space admins, you will need to use a Condition to check this.
atlassian-plugin.xml
1 2<!-- Item in Space Tools --> <web-item key="space-admin-quick-link-manager" name="Quick Link Manager in Space Admin" section="system.space.tools/addons" weight="100"> <label key="space.admin.quick.link.manager" /> <link linkId="space-admin-quick-link-manager-id">/plugins/${project.artifactId}/add-link.action?key=$generalUtil.urlEncode($helper.spaceKey)</link> <condition class="com.atlassian.confluence.plugin.descriptor.web.conditions.SpaceSidebarCondition"/> </web-item>
If your app needs to be backwards compatible with Confluence 5.x, you will need to add another web-item for the Space Admin area in the documentation theme:
atlassian-plugin.xml
1 2<!-- Item in Space Admin (for Doc Theme) --> <web-item key="space-admin-quick-link-manager-2" name="Quick Link Manager in Space Admin" section="system.space.admin/addons" weight="100"> <label key="space.admin.quick.link.manager" /> <link linkId="space-admin-quick-link-manager-id">/plugins/${project.artifactId}/add-link.action?key=$generalUtil.urlEncode($helper.spaceKey)</link> <condition class="com.atlassian.confluence.plugin.descriptor.web.conditions.SpaceSidebarCondition" invert="true"/> </web-item>
Next we need to declare an action in atlassian-plugin.xml
:
atlassian-plugin.xml
1 2<struts name="Example Actions" key="example-actions"> <description>Examples of actions</description> <package name="space-links-struts-package" extends="default" namespace="/plugins/unique/value"> <default-interceptor-ref name="validatingStack"/> <action name="add-link" class="com.atlassian.examples.MyAction" method="doCreate"> <result name="input" type="velocity">/templates/add-link-action.vm</result> <result name="success" type="velocity">/templates/add-link-action.vm</result> </action> <action name="mixed" class="com.atlassian.examples.MixedAction" method="doDefault"> <result name="input" type="velocity">/templates/mixed-space-screen.vm</result> </action> </package> </struts>
The action must extend SpaceAdminAction
. This will check whether the user has permissions for the space:
1 2public class MyAdminAction extends SpaceAdminAction { @Override public String doDefault() { return INPUT; } }
The template needs to use space.admin
decorator. You also need to provide the key of web-item to highlight in space admin/tools.
1 2<html> <head> <title>$action.getText("add.link.action")</title> <meta name="decorator" content="space.admin"/> <!-- Key of the web-item to highlight in Space Admin, discard this if you don't support pre-5.0 themes --> <content tag="space.admin.selected">space-admin-quick-link-manager-2</content> <!-- Key of the web-item to highlight in Space Tools --> <content tag="space.admin.tools.selected">space-admin-quick-link-manager</content> </head> <body> <div class="pagecontent"> <p>${message}</p> </div> </body> </html>
Here's the result:
First we need to add a web-item for Space Tools in atlassian-plugin.xml
. If the screen should only be visible to space admins, you will need to use a Condition to check this.
atlassian-plugin.xml
1 2<!-- Item in Space Tools --> <web-item key="space-admin-view-links" name="View links in Space Admin" section="system.space.tools/addons" weight="101"> <label key="space.admin.view.links" /> <link linkId="space-admin-view-links-id">/plugins/servlet/spacelinks?spaceId=$space.id</link> <condition class="com.atlassian.confluence.plugin.descriptor.web.conditions.SpaceSidebarCondition"/> </web-item>
If your app needs to be backwards compatible with Confluence 5.x, you will need to add another web-item for the Space Admin area in the documentation theme
atlassian-plugin.xml
1 2<!-- Item in Space Admin (for Doc Theme) --> <web-item key="space-admin-view-links-2" name="View links in Space Admin" section="system.space.admin/addons" weight="101"> <label key="space.admin.view.links" /> <link linkId="space-admin-view-links-id">/plugins/servlet/spacelinks?spaceId=$space.id</link> <condition class="com.atlassian.confluence.plugin.descriptor.web.conditions.SpaceSidebarCondition" invert="true"/> </web-item>
Next we need to add a servlet in atlassian-plugin.xml
.
atlassian-plugin.xml
1 2<servlet name="View links Servlet" key="viewlink-servlet" class="com.atlassian.examples.ViewLinkServlet"> <description>Display links.</description> <url-pattern>/spacelinks</url-pattern> </servlet>
The servlet needs to import some Confluence components:
pom.xml
1 2<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.atlassian.sal</groupId> <artifactId>sal-api</artifactId> <version>4.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.atlassian.templaterenderer</groupId> <artifactId>atlassian-template-renderer-api</artifactId> <version>4.0.0</version> <scope>provided</scope> </dependency>
atlassian-plugin.xml
1 2<component-import key="userManager" interface="com.atlassian.sal.api.user.UserManager"/> <component-import key="userAccessor" interface="com.atlassian.confluence.user.UserAccessor"/> <component-import key="loginUriProvider" interface="com.atlassian.sal.api.auth.LoginUriProvider"/> <component-import key="spaceManager" interface="com.atlassian.confluence.spaces.SpaceManager"/> <component-import key="templateRenderer" interface="com.atlassian.templaterenderer.TemplateRenderer"/>
ViewLinkServlet.java
1 2package com.atlassian.examples; import com.atlassian.confluence.spaces.Space; import com.atlassian.confluence.spaces.SpaceManager; import com.atlassian.confluence.user.ConfluenceUser; import com.atlassian.confluence.user.UserAccessor; import com.atlassian.sal.api.auth.LoginUriProvider; import com.atlassian.sal.api.user.UserKey; import com.atlassian.sal.api.user.UserManager; import com.atlassian.templaterenderer.TemplateRenderer; import com.google.common.collect.ImmutableMap; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.net.URI; import static java.util.Objects.requireNonNull; public class ViewLinkServlet extends HttpServlet { private static final String SPACE_ID_KEY = "spaceId"; private final UserManager userManager; private final UserAccessor userAccessor; private final LoginUriProvider loginUriProvider; private final SpaceManager spaceManager; private final TemplateRenderer templateRenderer; public ViewLinkServlet(UserManager userManager, UserAccessor userAccessor, LoginUriProvider loginUriProvider, SpaceManager spaceManager, TemplateRenderer templateRenderer) { this.userManager = requireNonNull(userManager); this.userAccessor = requireNonNull(userAccessor); this.loginUriProvider = requireNonNull(loginUriProvider); this.spaceManager = requireNonNull(spaceManager); this.templateRenderer = requireNonNull(templateRenderer); } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Find current user UserKey userKey = userManager.getRemoteUserKey(request); ConfluenceUser user = userAccessor.getExistingUserByKey(userKey); if (userKey == null || user == null) { redirectToLogin(request, response); return; } // Find space in the request Space space = null; String spaceIdString = request.getParameter(SPACE_ID_KEY); if (spaceIdString != null) { try { long spaceId = Long.parseLong(spaceIdString); space = spaceManager.getSpace(spaceId); } catch (NumberFormatException nfe) { // log a warning if needed } } if (space == null) { response.setStatus(HttpServletResponse.SC_NOT_FOUND); return; } response.setContentType("text/html;charset=utf-8"); templateRenderer.render("/templates/view-link-action.vm", ImmutableMap.of("spaceId", space.getId()), response.getWriter()); } private void redirectToLogin(HttpServletRequest request, HttpServletResponse response) throws IOException { response.sendRedirect(loginUriProvider.getLoginUri(getUri(request)).toASCIIString()); } private URI getUri(HttpServletRequest request) { StringBuffer builder = request.getRequestURL(); if (request.getQueryString() != null) { builder.append("?"); builder.append(request.getQueryString()); } return URI.create(builder.toString()); } }
Now, prepare the template for this servlet. The template needs to specify space.admin
as the decorator. You also need to provide the spaceId of the current space and the key of the web-item to highlight in space admin/tools.
view-link-action.vm
1 2<html> <head> <title>View links</title> <meta name="decorator" content="space.admin"/> <meta name="spaceid" content="$spaceId"/> <!-- Key of the web-item to highlight in Space Admin --> <content tag="space.admin.selected">space-admin-view-links-2</content> <!-- Key of the web-item to highlight in Space Tools --> <content tag="space.admin.tools.selected">space-admin-view-links/content> </head> <body> <div class="pagecontent"> <p>Your links should appear here</p> </div> </body> </html>
Here is the result:
The space.admin
decorator is only available in Confluence 7.5 and later. For earlier versions, you'll need to use Velocity and apply the 2 decorators in the way we've described below.
You need two web-items, for Space Tools (5.x and later themes) and Space Admin (pre-5.0 themes). If the screen should only be visible to space admins, you will need to use a Condition to check this.
atlassian-plugin.xml
1 2<!-- Item in Space Tools --> <web-item key="space-admin-quick-link-manager" name="Quick Link Manager in Space Admin" section="system.space.tools/addons" weight="100"> <label key="space.admin.quick.link.manager" /> <link linkId="space-admin-quick-link-manager-id">/plugins/${project.artifactId}/add-link.action?key=$generalUtil.urlEncode($helper.spaceKey)</link> <conditions type="AND"> <condition class="com.atlassian.confluence.plugin.descriptor.web.conditions.SpacePermissionCondition"> <param name="permission">administer</param> </condition> <condition class="com.atlassian.confluence.plugin.descriptor.web.conditions.SpaceSidebarCondition"/> </conditions> </web-item> <!-- Item in Space Admin (for Doc Theme) --> <web-item key="space-admin-quick-link-manager-2" name="Quick Link Manager in Space Admin" section="system.space.admin/addons" weight="100"> <label key="space.admin.quick.link.manager" /> <link linkId="space-admin-quick-link-manager-id">/plugins/${project.artifactId}/add-link.action?key=$generalUtil.urlEncode($helper.spaceKey)</link> <conditions type="AND"> <condition class="com.atlassian.confluence.plugin.descriptor.web.conditions.SpacePermissionCondition"> <param name="permission">administer</param> </condition> <condition class="com.atlassian.confluence.plugin.descriptor.web.conditions.SpaceSidebarCondition" invert="true"/> </conditions> </web-item>
For this example, we will also declare an action:
atlassian-plugin.xml
1 2<struts name="Example Actions" key="example-actions"> <description>Examples of actions</description> <package name="space-links-struts-package" extends="default" namespace="/plugins/unique/value"> <default-interceptor-ref name="validatingStack"/> <action name="add-link" class="com.atlassian.examples.MyAdminAction"> <result name="input" type="velocity">/templates/add-link-action.vm</result> <result name="success" type="velocity">/templates/add-link-action.vm</result> </action> </package> </struts>
The action must extend SpaceAdminAction
, this will check whether the user has permissions for the space:
1 2public class MyAdminAction extends SpaceAdminAction { @Override public String doDefault() { return INPUT; } }
The template needs two decorators:
add-link-action.vm
1 2<html> <head> <title>$action.getText("add.link.action")</title> <meta name="decorator" content="main"/> </head> #applyDecorator("root") #decoratorParam("helper" $action.helper) ## Name of the tab to highlight: space-operations is also valid. #decoratorParam("context" "space-administration") #applyDecorator ("root") ## The .vmd to use - This one displays both in Space Admin and Space Tools. #decoratorParam ("context" "spaceadminpanel") ## Key of the web-item to highlight in Space Admin #decoratorParam ("selection" "quick-link-manager-2") ## Key of the web-item to highlight in Space Tools #decoratorParam ("selectedSpaceToolsWebItem" "quick-link-manager") #decoratorParam ("helper" $action.helper) <body> <div class="pagecontent"> <p>${message}</p> </div> </body> #end #end </html>
Context:
Here is the result:
The example above is also compatible with the Doc Theme and other pre-5.0 themes:
Rate this page: