Last updated Mar 22, 2024

Adding content to the Jira View Issue page

Level of experience:

Beginner. This is a good tutorial to try if you have never developed an app before.

Time estimate:

It should take you approximately 1 hour to complete this tutorial.

Applicable:

Jira 7.0.0 and later

Overview of the tutorial

This tutorial shows you how to add content to the View Issue page in Jira. It demonstrates how to create an app that adds a web panel to the page if the issue has a due date. Web panel displays a colorful indication of when an issue is due (or overdue).

Completed app consists of the following components:

  1. An app descriptor (XML file) that describes the plugin module to Jira.
  2. A Java DueDateIndicator class that calculates the due date for an issue and exposes it as an object context that we can use in Jira UI templates.
  3. Resources for displaying the app UI. This includes an internationalization properties tutorial-jira-add-content-to-view-issue-screen.properties file and a Velocity template (that is, the due-date-indicator.vm file) for rendering the web panel plugin module.

When you are finished, all these components are packaged in a single JAR file. 

About these instructions

You can use any supported combination of operating system and IDE to create this app. These instructions were written using IntelliJ IDEA 2017.2 on macOS Sierra. If you use another operating system or IDE combination, you should use the equivalent operations for your specific environment. This tutorial was last tested with Jira 7.7.1 using the Atlassian SDK 6.3.10.

Before you begin

To complete this tutorial, you need to know the following: 

  1. The basics of Java development: classes, interfaces, methods, how to use the compiler, and so on.
  2. How to create an Atlassian plugin project using the Atlassian Plugin SDK.
  3. The basics of using and administering Jira.

App source

We encourage you to work through this tutorial. If you want to skip ahead or check your work when you have finished, you can find the app source code on Atlassian Bitbucket.

To clone the repository, run the following command:

1
2
git clone https://bitbucket.org/atlassian_tutorial/jira-add-content-to-view-issue-screen

Alternatively, you can download the source as a ZIP archive.

Step 1. Create the app project

In this step, you'll use two atlas- commands to generate stub code for your app. The atlas- commands are part of the Atlassian Plugin SDK and automate much of the work of app development for you.

  1. Set up the Atlassian Plugin SDK and Build a Project if you have not done it yet.

  2. Open a Terminal on your machine and navigate to directory where you would like to keep your app code.

  3. To create an app skeleton, run the following command:

    1
    2
    atlas-create-jira-plugin
    
  4. To identify your app, enter the following information.

    group-id

    com.example.plugins.tutorial

    artifact-id

    tutorial-jira-add-content-to-view-issue-screen

    version

    1.0-SNAPSHOT

    package

    com.example.plugins.tutorial

  5. Confirm your entries when prompted.

  6. Navigate to the tutorial-jira-add-content-to-view-issue-screen directory created in the previous step.

  7. Delete the test directories.

    1
    2
    rm -rf ./src/test/java
    rm -rf ./src/test/resources/
    
  8. Delete the unneeded Java class files.

    1
    2
    rm -rf ./src/main/java/com/example/plugins/tutorial/*
    
  9. Import the project into your favorite IDE.

Step 2. Review and tweak the generated stub code

It is a good idea to familiarize yourself with the project configuration file known as the POM (that is, Project Object Model definition file). In this section, you will review and tweak the pom.xml file.

Add app metadata to the POM

The POM is located at the root of your project and declares the project dependencies and other information.

  1. In the root folder of your app, open the pom.xml file.

  2. In the organization element, add your company or organization name and your website URL. The following code block shows how it looks in plain text:

    1
    2
    <organization>
        <name>Example Company</name>
        <url>http://www.example.com/</url>
    </organization>
    
  3. Update the description element with the following:

    1
    2
    <description>This plugin shows the due date for an issue in a new panel on the View Issue page.</description>
    
  4. Modify the name element to be something more readable (optional):

    1
    2
    <name>Due Date Indicator on the 'View Issue' Page</name>
    

    This is the name for your app that will appear on the Jira app administration page.

  5. Save the file.

Review the generated app descriptor

Your stub code contains an app descriptor file atlassian-plugin.xml. This is an XML file that identifies the app to the host application (Jira) and defines the required app functionality.

  1. In your IDE, navigate to src/main/resources and open the descriptor file.

    You should see something like this (comments removed):

1
2
<atlassian-plugin key="${atlassian.plugin.key}" name="${project.name}" plugins-version="2">
    <plugin-info>
        <description>${project.description}</description>
        <version>${project.version}</version>
        <vendor name="${project.organization.name}" url="${project.organization.url}" />
        <param name="plugin-icon">images/pluginIcon.png</param>
        <param name="plugin-logo">images/pluginLogo.png</param>
    </plugin-info>
    <resource type="i18n" name="i18n" location="tutorial-jira-add-content-to-view-issue-screen"/>
    <web-resource key="tutorial-jira-add-content-to-view-issue-screen-resources" name="tutorial-jira-add-content-to-view-issue-screen Web Resources">
        <dependency>com.atlassian.auiplugin:ajs</dependency>
        <resource type="download" name="tutorial-jira-add-content-to-view-issue-screen.css" location="/css/tutorial-jira-add-content-to-view-issue-screen.css"/>
        <resource type="download" name="tutorial-jira-add-content-to-view-issue-screen.js" location="/js/tutorial-jira-add-content-to-view-issue-screen.js"/>
        <resource type="download" name="images/" location="/images"/>
        <context>tutorial-jira-add-content-to-view-issue-screen</context>
    </web-resource>
</atlassian-plugin>

Note that some of the information from the POM is transferred to the app descriptor using variable names such as ${project.name}.

Step 3. Add your plugin modules

Now you will use the plugin module generator (that is, another atlas- command) to generate the stub code for modules that the app needs.

For this step, you will need a web panel plugin module. You'll add this using the atlas-create-jira-plugin-module command. The command can also generate an i18n properties file for your Jira UI text, which we'll add as well.

  1. Open a Terminal, navigate to the app root folder where the pom.xml is located, and then run the following command:

    1
    2
    atlas-create-jira-plugin-module
    
  2. Select the Web Panel option, and then add the following information.

    Enter Plugin Module Name

    DueDateIndicator

    Enter Location

    atl.jira.view.issue.right.context

    For more information, see Right Side of the 'View Issue' Page Location.

  3. Select 'y' for the Show Advanced Setup prompt.

  4. For the next five options, you can accept the default by selecting enter. For reference, the values are the following.

    Module Key

    due-date-indicator

    Module Description

    The DueDateIndicator Plugin

    i18n Name Key

    due-date-indicator.name

    i18n Description Key

    due-date-indicator.description

    Weight

    1000

  5. Select 'y' for Add Resource prompt, and then enter the following information for the resource.

    Enter Resource Name

    view

    Enter Resource Type

    velocity

    Enter Location (path to resource file)

    templates/due-date-indicator.vm

  6. Select 'N' for Add Resource Parameter prompt.

  7. Select 'N' for Add Resource.

  8. Select 'y' for Add Velocity Context Provider.

  9. For Add Fully Qualified Context Provider Class prompt enter the following: com.example.plugins.tutorial.DueDateIndicator.

  10. Select 'N' for Add Conditions.

  11. Select 'N' for Add Another Plugin Module.

The SDK finishes generating your plugin module and returns you to the command prompt.

Step 4. Add a panel title

The SDK gives us most of what we need for the app configuration, but we'll need to add a title for our panel manually. 

  1. Navigate to src/main/resources and open the atlassian-plugin.xml file.

  2. Add the following label element as a child of web-panel element:

    1
    2
    <web-panel name="DueDateIndicator" ...>
        ...
            <label key="due-date-indicator.title"/>
    </web-panel>
    

    The key parameter value represents a key in your i18n properties file.

    Your app descriptor file should look something like this (comments removed):

    1
    2
    <?xml version="1.0" encoding="UTF-8"?>
    <atlassian-plugin key="${atlassian.plugin.key}" name="${project.name}" plugins-version="2">
      <plugin-info>
        <description>${project.description}</description>
        <version>${project.version}</version>
        <vendor name="${project.organization.name}" url="${project.organization.url}"/>
        <param name="plugin-icon">images/pluginIcon.png</param>
        <param name="plugin-logo">images/pluginLogo.png</param>
      </plugin-info>
      <resource type="i18n" name="i18n" location="tutorial-jira-add-content-to-view-issue-screen"/>
      <web-resource key="tutorial-jira-add-content-to-view-issue-screen-resources" name="tutorial-jira-add-content-to-view-issue-screen Web Resources">
        <dependency>com.atlassian.auiplugin:ajs</dependency>
        <resource type="download" name="tutorial-jira-add-content-to-view-issue-screen.css" location="/css/tutorial-jira-add-content-to-view-issue-screen.css"/>
        <resource type="download" name="tutorial-jira-add-content-to-view-issue-screen.js" location="/js/tutorial-jira-add-content-to-view-issue-screen.js"/>
        <resource type="download" name="images/" location="/images"/>
        <context>tutorial-jira-add-content-to-view-issue-screen</context>
      </web-resource>
    
      <web-panel name="DueDateIndicator" i18n-name-key="due-date-indicator.name" key="due-date-indicator" location="atl.jira.view.issue.right.context" weight="1000">
        <description key="due-date-indicator.description">The DueDateIndicator Plugin</description>
        <context-provider class="com.example.plugins.tutorial.DueDateIndicator"/>
        <resource name="view" type="velocity" location="templates/due-date-indicator.vm"/>
        <label key="due-date-indicator.title"/>
      </web-panel>
    </atlassian-plugin>
    
  3. Save and close the file.

  4. In the same directory, open the i18n properties file called tutorial-jira-add-content-to-view-issue-screen.properties.

  5. To specify the value of the due-date-indicator.title key you just defined in your app descriptor, add the following due-date-indicator.title property:

    1
    2
    due-date-indicator.title=Due Date Indicator
    

    In the Jira View Issue page, this value will appear as the title of your new web panel. Your i18n properties file should look like this:

    1
    2
    #put any key/value pairs here
    my.plugin.name=MyPlugin
    due-date-indicator.name=DueDateIndicator
    due-date-indicator.description=The DueDateIndicator Plugin
    due-date-indicator.title=Due Date Indicator
    
  6. Save and close the file.

Step 5. Write your Java class

So far, you've created the framework for your web panel plugin module. Now we'll write some Java code that makes the web panel do something interesting. Namely it:

  1. Retrieves the due date associated with the current issue.
  2. Calculates the difference between the current data and the due date.

We start with a little refactoring of the code that the SDK gave us.

  1. Navigate to src/main/java/com/example/plugins/tutorial directory, create and open a new DueDateIndicator.java file.

    1
    2
    package com.example.plugins.tutorial;
    
    import com.atlassian.jira.issue.Issue;
    import com.atlassian.jira.plugin.webfragment.contextproviders.AbstractJiraContextProvider;
    import com.atlassian.jira.plugin.webfragment.model.JiraHelper;
    import com.atlassian.jira.user.ApplicationUser;
    
    import java.sql.Timestamp;
    import java.time.LocalDate;
    import java.util.HashMap;
    import java.util.Map;
    
    import static java.time.temporal.ChronoUnit.DAYS;
    
    public class DueDateIndicator extends AbstractJiraContextProvider {
    
        @Override
        public Map<String, Object> getContextMap(ApplicationUser applicationUser, JiraHelper jiraHelper) {
            return null;
        }
    }
    

    Notice that it now extends the abstract class AbstractJiraContextProvider and implements the class' getContextMap method.

  2. The getContextMap method that we implemented needs to return a Map object with a key and value that represents the number of days between the due date and the current date, so that it can be used by other parts of the module.

    To do this, initialize a new HashMap Map object within the contextMap method to store this key and value.

    1
    2
    Map<String, Object> contextMap = new HashMap<>();
    
  3. Within the getContextMap method, initialize a new Jira Issue object currentIssue. It will store the current Jira issue as an object through which you can access its due date as a Java Timestamp using the getDueDate() method.

    1
    2
    Issue currentIssue = (Issue) jiraHelper.getContextParams().get("issue");
    Timestamp dueDate = currentIssue.getDueDate();
    
  4. Check to see if the current issue's due date has been set and, if so, begin calculating the number of days between the current date and due date. We use Java Date/Time API to calculate days between due date and current date.

    1
    2
    if (dueDate != null) {
        LocalDate currentTimeInDays = LocalDate.now();
        LocalDate dueDateTimeInDays = dueDate.toLocalDateTime().toLocalDate();
    }
    
  5. Now that we have the number of days for both the due date time (dueDateTimeInDays) and current date time (currentTimeInDays), we need to calculate the difference between these values.

    Associate the daysAwayFromDueDateCalc value with the keyword daysAwayFromDueDate using the Map object (that is, contextMap). Within the if statement mentioned in the previous step, add the following lines of code:

    1
    2
    long daysAwayFromDueDateCalc = DAYS.between(currentTimeInDays, dueDateTimeInDays);
    contextMap.put("daysAwayFromDueDate", daysAwayFromDueDateCalc);
    
  6. The DueDateIndicator class should return the contextMap Map object back to your web panel plugin module to make it available to other resources in your app (that is, Velocity Template resource, which you will define in the next step). To return the object, add the following line:

    1
    2
    return contextMap;
    
  7. Save the file.

The complete code for DueDateIndicator should look like this:

1
2
package com.example.plugins.tutorial;

import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.plugin.webfragment.contextproviders.AbstractJiraContextProvider;
import com.atlassian.jira.plugin.webfragment.model.JiraHelper;
import com.atlassian.jira.user.ApplicationUser;

import java.sql.Timestamp;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.Map;

import static java.time.temporal.ChronoUnit.DAYS;

public class DueDateIndicator extends AbstractJiraContextProvider {

    @Override
    public Map<String, Object> getContextMap(ApplicationUser applicationUser, JiraHelper jiraHelper) {
        Map<String, Object> contextMap = new HashMap<>();
        Issue currentIssue = (Issue) jiraHelper.getContextParams().get("issue");
        Timestamp dueDate = currentIssue.getDueDate();
        if (dueDate != null) {
            LocalDate currentTimeInDays = LocalDate.now();
            LocalDate dueDateTimeInDays = dueDate.toLocalDateTime().toLocalDate();
            long daysAwayFromDueDateCalc = DAYS.between(currentTimeInDays, dueDateTimeInDays);
            contextMap.put("daysAwayFromDueDate", daysAwayFromDueDateCalc);
        }
        return contextMap;
    }
}

Step 6. Write your Velocity template

So far, you've written a Java class that retrieves the difference in days between the current issue due date and the current date. Now you will write a Velocity template to present this information in HTML format.

Your web panel plugin module already contains the following context provider:

1
2
<context-provider class="com.example.plugins.tutorial.DueDateIndicator"/>

This is your DueDateIndicator Java class. As already coded, this class returns a Map object with the property daysAwayFromDueDate whose value is the number of days between the current issue's due date and the due date.

The daysAwayFromDueDate property is available to other resources in the plugin module, such as the Velocity template resource that you already defined in the app descriptor using the SDK due-date-indicator.vm. However, while we declared it in the descriptor, we still need to add the file.

  1. Navigate to src/main/resources, create a new templates directory, and then create a new due-date-indicator.vm file there.

  2. Add the following if/then/else statement:

    1
    2
    #set ($overdueDays = $daysAwayFromDueDate * -1)
    #if ($daysAwayFromDueDate)
       #if ($daysAwayFromDueDate > 1)
           <span style="font-weight: bold; color: green;">This issue is due in $daysAwayFromDueDate days.</span>
       #elseif ($daysAwayFromDueDate == 1)
           <span style="font-weight: bold; color: blue;">This is issue is due tomorrow.</span>
       #elseif ($daysAwayFromDueDate == 0)
           <span style="font-weight: bold; color: purple;">This issue is due today.</span>
       #elseif ($daysAwayFromDueDate == -1)
           <span style="font-weight: bold; color: #ff4500;">This issue was due yesterday!</span>
       #elseif ($daysAwayFromDueDate < -1)
           <span style="font-weight: bold; color: red;">This issue is overdue by $overdueDays days!</span>
       #end
    #end
    

    This statement provides a colorful visual indicator of when the current issue is due.

    Notice that we've defined a new variable called $overdueDays. Because the daysAwayFromDueDate property returned by our Java class contains a negative value whenever an issue is overdue (which is what our if/then/else statement series relies upon), we've created this new variable to present the value of daysAwayFromDueDate as a positive number. Also note that it is a deep-red orange color.

  3. Save the file.

Step 7. Build, install, and run the app

Now you're ready to test your app.

  1. In a new Terminal window, navigate to the project root directory.

  2. Run the following Atlassian Plugin SDK command:

    1
    2
    atlas-run
    

    This builds your app code, starts a Jira instance, and installs your app. This could take a few minutes.

  3. Go to localhost:2990/jira in your browser.

  4. Log in using the default admin/admin.

  5. If you use a recent version of Jira, you'll see a wizard for creating a new Jira project. Follow the steps to create one. 

  6. Create a new issue, add a due date for it, and then save.

  7. Go to your new issue, and voila! You will see your Due Date Indicator web panel.

Congratulations, that's it!

Have a treat!

Next steps

To learn more about Jira issues, go to the following pages:

Rate this page: