Last updated Dec 5, 2024

JIRA Developer Documentation : JIRA Agile LinkProvider Plugin Module

Available:

JIRA Agile 5.3 and later

This module allows you to add links to the JIRA Agile context menus (internally referred to as the cog actions).

This plugin module only works with Classic Boards. As yet there is not an equivalent plugin point for the new boards.

Configuration

The root element for the LinkProvider plugin module is gh-link-provider. It allows the following attributes and child elements for configuration:

Attributes

Name

Required

Description

Default

class

Yes

Name of the class that implements the LinkProvider interface.

 

key

Yes

The identifier of the plugin module.

 

name

 

The human-readable name of the plugin module. I.e. the human-readable name of the links this provider adds.

 

Elements

No elements are supported currently.

Java API

The actual logic has to be implemented in a Java class implementing the LinkProvider interface. Two other objects, LinkContext and Link are also of interest.

LinkProvider interface

Following interface needs to be implemented by link providers:

1
2
package com.atlassian.greenhopper.plugin.link;

import java.util.List;

/**
 * Interface to be implemented by plugins that want to add links to the GreenHopper action context menus.
 *
 * @author miruflin
 *
 */
public interface LinkProvider
{
    /**
     * Get the list of links to be displayed for the given context.
     *
     * @param linkContext context information such as the current board and issue
     * @return a list of links to be displayed. null or an empty list if no links should be displayed.
     */
    public List<Link> getLinks(LinkContext linkContext);
}

LinkContext object

Following context is provided to the LinkProvider in order to determine what element on the page the link is about:

1
2
package com.atlassian.greenhopper.plugin.link;

import com.atlassian.jira.project.Project;
import com.opensymphony.user.User;
/**
 * Context object provided to LinkProvider objects.
 *
 * @author miruflin
 *
 */
public interface LinkContext
{
    /**
     * Get the project.
     *
     * @return the project object
     */
    public Project getProject();
    /**
     * Get the current user.
     *
     * @return the user, might be null
     */
    public User getUser();
    /**
     * Get the id of the board.
     *
     * @return the id, commonly -1 for none, the username for type AssigneeBoard, the version for type VersionBaord, the component for type ComponentBoard
     */
    public String getBoardId();
    /**
     * Get the board type.
     *
     * @return the name of the board, e.g. versionBoard.
     */
    public String getBoardType();
    /**
     * Get the issue id.
     *
     * @return the issue id or null if not applicable
     */
    public Long getIssueId();
    /**
     * Get the issue key.
     *
     * @return the issue key or null if not applicable
     */
    public String getIssueKey();
    /**
     * Assignee "filter" value.
     * Note that this does not apply to the assignee board, in which case the board id is the selected assignee.
     *
     * @return the assignee drop down value
     */
    public String getByAssignee();
    /**
     * Version "filter" value.
     * Note that this does not apply to the version board, in which case the board id is the selected version.
     *
     * @return the version drop down value
     */
    public String getByVersion();
}

The expected Link objects look as follows:

1
2
package com.atlassian.greenhopper.plugin.link;

/**
 * Link object displayed in one of the GreenHopper context menus.
 *
 * @author miruflin
 */
public class Link
{
    /** The href attribute content. */
    private String href;

    /** The title of the link. */
    private String title;

    /**
     * Get the href value
     */
    public String getHref()
    {
        return href;
    }

    /**
     * Set the href attribute value.
     * The value can be a full url, a relative url or even some javascript of the form "javascript:alert('hello');"
     *
     * @param href the value to set
     */
    public void setHref(String href)
    {
        this.href = href;
    }

    /**
     * Get the link title
     */
    public String getTitle()
    {
        return title;
    }

    /**
     * Set the link title
     *
     * @param text the title of the link
     */
    public void setTitle(String text)
    {
        this.title = text;
    }

    /**
     * Should the href value be prefixed by the web application base url?
     *
     * The default implementation returns true if the href value starts with a slash.
     *
     * @return true if the base url should be added, false otherwise.
     */
    public boolean isAddBaseUrl()
    {
        return href != null && href.startsWith("/");
    }
}

Example

Here is an example atlassian-plugin.xml file containing a single link provider:

1
2
<gh-link-provider key="lhp-box-menu-item" name="LHP box menu item" class="com.pyxis.jira.greenhopper.GreenHopperMenuItems" />

class in one word (the attribute gets removed!).

The following code is the implementation of the GreenHopperMenuItems for the Link Hierarchy plugin:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package com.pyxis.jira.greenhopper;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.atlassian.greenhopper.plugin.link.Link;
import com.atlassian.greenhopper.plugin.link.LinkContext;
import com.atlassian.greenhopper.plugin.link.LinkProvider;
import com.atlassian.jira.ComponentManager;
import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.issue.IssueManager;
import com.atlassian.jira.issue.link.IssueLinkType;
import com.atlassian.jira.project.version.Version;
import com.atlassian.jira.project.version.VersionManager;
import com.pyxis.greenhopper.GreenHopper;
import com.pyxis.greenhopper.jira.boards.ArchivedChartBoard;
import com.pyxis.greenhopper.jira.boards.ChartBoard;
import com.pyxis.greenhopper.jira.boards.VersionBoard;

public class GreenHopperMenuItems implements LinkProvider
{
    public static final Logger log = Logger.getLogger(GreenHopperMenuItems.class);

    private final GreenHopper ghService;
    private final VersionManager versionManager;
    private final IssueManager issueManager;

    public GreenHopperMenuItems(VersionManager versionManager, IssueManager issueManager)
    {
        this.ghService = (GreenHopper)ComponentManager.getOSGiComponentInstanceOfType(GreenHopper.class);
        this.versionManager = versionManager;
        this.issueManager = issueManager;
    }

    public List<Link> getLinks(LinkContext linkContext)
    {
        try
        {
            if(!StringUtils.isEmpty(linkContext.getIssueKey())) // Display in the issue menu
            {
                Issue issue = issueManager.getIssueObject(linkContext.getIssueKey());
                if(issue != null)
                {
                    IssueLinkType linkType = ghService.getConfiguration(issue.getProjectObject()).getLinkType();
                    if(linkType != null)
                    {
                        return Arrays.asList(new Link[]{getLink(issue, linkType)});
                    }
                }
            }
            else if(isBoardSupported(linkContext.getBoardType())) // Display in the RHS column boxes menu
            {
                Version version = versionManager.getVersion(Long.valueOf(linkContext.getBoardId()));
                IssueLinkType linkType = ghService.getConfiguration(version.getProjectObject()).getLinkType();
                if(version != null && linkType != null)
                {
                    return Arrays.asList(new Link[]{getLink(version, linkType)});
                }
            }
        }
        catch (Exception e)
        {
            log.error(e);
        }

        return new ArrayList<Link>();
    }

    private boolean isBoardSupported(String boardType)
    {
        return VersionBoard.VIEW.equals(boardType) || ChartBoard.VIEW.equals(boardType) || ArchivedChartBoard.VIEW.equals(boardType);
    }

    private Link getLink(Version version, IssueLinkType linkType)
    {
        StringBuilder sb = new StringBuilder("/secure/ConfigureReport.jspa");
        sb.append("?selectedProjectId=").append(version.getProjectObject().getId());
        sb.append("&pid=").append(version.getProjectObject().getId());
        sb.append("&versionId=").append(version.getId());
        sb.append("&linkTypeId=").append(linkType.getId());
        sb.append("&reportKey=").append("com.pyxis.jira.links.hierarchy.reports:pyxis.hierarchy.report.version");
        sb.append("&subtasks=true");
        sb.append("&orphans=true");
        sb.append("&hierarchyView=tree");
        sb.append("&Next=Next");

        Link link = new Link();
        link.setHref(sb.toString());
        link.setTitle(linkType.getName());
        return link;
    }

    private Link getLink(Issue issue, IssueLinkType linkType)
    {
        StringBuilder sb = new StringBuilder("/secure/ConfigureReport.jspa");
        sb.append("?selectedProjectId=").append(issue.getProjectObject().getId());
        sb.append("&pid=").append(issue.getProjectObject().getId());
        sb.append("&issueKey=").append(issue.getKey());
        sb.append("&linkTypeId=").append(linkType.getId());
        sb.append("&reportKey=").append("com.pyxis.jira.links.hierarchy.reports%3Apyxis.hierarchy.report.issue");
        sb.append("&subtasks=true");
        sb.append("&orphans=true");
        sb.append("&Next=Next");

        Link link = new Link();
        link.setHref(sb.toString());
        link.setTitle(linkType.getName());
        return link;
    }
}

Rate this page: