Last updated Apr 11, 2024

Creating a custom field type

Level of experience:

Beginner.

Time estimate:

It should take you about 30 minutes to complete this tutorial.

Applicable:

This tutorial applies to Jira 7.1.0 and higher.

Tutorial overview

This tutorial shows you how to create a new custom field type for Jira using the app development platform. You'll create a custom field that can be edited only by admin user. Your custom field will be written and installed as a Jira app, and include a Java class that extends the GenericTextCFType class.

To render your field and control who can edit or view it, you'll use a Velocity template. It is a format that combines Java and HTML. To bind your Java class and template together, you'll define them both in a single customfield-type plugin module in your atlassian-plugin.xml descriptor file.

This tutorial covers the following topics:

  1. An overview of custom fields and the files that comprise the app.
  2. Extending the custom field type class GenericTextCFType for your app.
  3. Using the customfield-type plugin module type for Jira.

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.3 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 should:

  1. Understand the basics of Java development: classes, interfaces, methods, how to use the compiler, and so on.
  2. Know the basics of using and administering Jira.

You should be able to complete this tutorial even if you've never created an app before.

App source

We encourage you to work through the tutorial. If you'd like to skip ahead or check your work when you're done, you can find the app source on Atlassian Bitbucket.

To clone the repository for this app, run the following command: 

1
2
git clone https://bitbucket.org/atlassian_tutorial/jira-custom-field-tutorial.git

Alternatively, you can download the source code for this project as ZIP archive.

Step 1. Create the app project and trim the skeleton

In this step you'll generate a Jira app skeleton. You'll use atlas- commands to automate the app creation. You won't need some files that are automatically generated by the SDK, so you'll delete them in this step.

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

  2. To create a Jira app skeleton, run the following command: 

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

    group-id

    com.example.plugins.tutorial.customfields

    artifact-id

    jira-custom-field-example

    version

    1.0-SNAPSHOT

    package

    com.example.plugins.tutorial.customfields

  4. When prompted, confirm your entries with Y or y.

  5. Navigate to the jira-custom-field-example directory created in the previous step.

    1
    2
    cd jira-custom-field-example
    
  6. To delete the test directories, run the following commands:

    1
    2
    rm -rf ./src/test/java/
    rm -rf ./src/test/resources/
    
  7. Delete the unnecessary Java files. 

    1
    2
    rm -rf ./src/main/java/com/example/plugins/tutorial/customfields/*
    

    You'll build a Java class for your app in next steps.

  8. Import the project into your favorite IDE.

Step 2. Create your JiraCustomField class

Your custom field extends the Jira GenericTextCFType  class. 

GenericTextCFType is one of many custom field-specific Java classes and interfaces. Other examples include:

The GenericTextCFType class stores and retrieves field values as strings. In this step, you'll extend the class for your app, and in future steps you'll apply your field logic in Velocity template so that only admins can modify the string value.

  1. Create a new JiraCustomField class that extends GenericTextCFType with following stub code: 
1
2
package com.example.plugins.tutorial.customfields;

import com.atlassian.jira.issue.customfields.impl.GenericTextCFType;
import com.atlassian.jira.issue.customfields.manager.GenericConfigManager;
import com.atlassian.jira.issue.customfields.persistence.CustomFieldValuePersister;
import com.atlassian.jira.issue.fields.TextFieldCharacterLengthValidator;
import com.atlassian.jira.security.JiraAuthenticationContext;
import com.atlassian.plugin.spring.scanner.annotation.component.Scanned;
import com.atlassian.plugin.spring.scanner.annotation.imports.JiraImport;

@Scanned
public class JiraCustomField extends GenericTextCFType {

    public JiraCustomField(
            @JiraImport CustomFieldValuePersister customFieldValuePersister,
            @JiraImport GenericConfigManager genericConfigManager,
            @JiraImport TextFieldCharacterLengthValidator textFieldCharacterLengthValidator,
            @JiraImport JiraAuthenticationContext jiraAuthenticationContext) {
        super(customFieldValuePersister, genericConfigManager, textFieldCharacterLengthValidator , jiraAuthenticationContext);
    }
}

Step 3. Create a Velocity template to format your custom field

In this step you'll write the logic for rendering custom field in a Velocity template.

You'll create a condition to check if the users' group name includes 'jira-administrators'. If users fit the criteria, they'll be able to edit the field value. All other users will be able to see the value entered, but they will be unable to change it.

You'll also create an else condition that if no value has been set for the field, Jira will display "N/A" as the default value. 

  1. Under src/main/resources, create a templates directory.

    1
    2
    mkdir src/main/resources/templates
    
  2. Under your new templates directory, create an edit.vm file with the following code:

    1
    2
    #controlHeader ($action $customField.id $customField.name $fieldLayoutItem.required $displayParameters.noHeader)
    #if ($jiraUserUtils.getGroupNamesForUser($authcontext.loggedInUser.name).contains('jira-administrators'))
    <input type="text"
           name="$customField.id"
           value="$!value" />
    #else
        #if($value && ! $value.equals(""))
            #set ($displayValue = ${value})
        #else
            #set ($displayValue = 'N/A')
        #end
    <span title="This field is editable only by JIRA administrators">$!displayValue</span>
    <input type="hidden"
           name="$customField.id"
           value="$!value" />
    #end
    #controlFooter ($action $fieldLayoutItem.fieldDescription $displayParameters.noHeader)
    

    Here we add the conditional check to see if the user is an admin. If no value exists, the user will see "N/A".

Step 4. Add a customfield-type plugin module to your descriptor file

In this step you'll complete your app code with the addition of a customfield-type plugin module. You'll add this module to the atlassian-plugin.xml descriptor file. The descriptor file was automatically generated when you built your app skeleton. It describes modules and resources provided by your plugin to the Atlassian system.

Inserting a customfield-type plugin module lets you add your custom field to Jira. This module requires a Java class for implementation and a unique key identifier.

You'll also reference your Velocity template as a resource type element. There are four view types for any custom field, three of which are required. 

View typeDetails
viewProvides a basic read-only view of the field value.
column-viewRead-only view for displaying in the issue navigator. The issue navigator will display the view value if omitted.
editRenders the edit widget in issue creation, editing issues, and editing defaults. This is the value you'll replace with your custom Velocity template.
xmlShows the value in XML format for RSS or XML exports.

*view, edit, and xml view types are required.

You'll use two built-in Velocity templates in Jira for the view and xml view types, and your custom template for the edit value. 

  1. Navigate to src/main/resources and open the atlassian-plugin.xml file. Before the closing </atlassian-plugin>  tag, add the following block:

    1
    2
    <customfield-type name="Jira Custom Field" i18n-name-key="jira-custom-field.name" key="admintextfield" class="com.example.plugins.tutorial.customfields.JiraCustomField">
    </customfield-type>
    

    You'll give your custom field a unique key, name, and define JiraCustomField as the implementing Java class.

  2. Inside the first customfield-type tag, add the following description: 

    1
    2
    <description>A text field only editable by those with admin permissions</description>
    

    This description appears in Jira when you apply the custom field type from the admin pages. 

    You can also specify category for your custom field type. Available values are STANDART and ADVANCED, default is ADVANCED.

  3. Define which Velocity templates to use for each view type: 

    1
    2
    <resource type="velocity" name="view" location="templates/plugins/fields/view/view-basictext.vm"/>
    <resource type="velocity" name="edit" location="templates/edit.vm"/>
    <resource type="velocity" name="xml" location="templates/plugins/fields/xml/xml-basictext.vm"/>
    
  4. Save and close the descriptor file.

    Here's the completed atlassian-plugin.xml descriptor file.

    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>
        <!-- add our i18n resource -->
        <resource type="i18n" name="i18n" location="jira-custom-field-example"/>
        <!-- add our web resources -->
        <web-resource key="jira-custom-field-example-resources" name="jira-custom-field-example Web Resources">
            <dependency>com.atlassian.auiplugin:ajs</dependency>
            <resource type="download" name="jira-custom-field-example.css" location="/css/jira-custom-field-example.css"/>
            <resource type="download" name="jira-custom-field-example.js" location="/js/jira-custom-field-example.js"/>
            <resource type="download" name="images/" location="/images"/>
            <context>jira-custom-field-example</context>
        </web-resource>
        <customfield-type name="Jira Custom Field" i18n-name-key="jira-custom-field.name" key="admintextfield" class="com.example.plugins.tutorial.customfields.JiraCustomField">
            <description>A text field only editable by those with admin permissions</description>
            <resource name="view" type="velocity" location="templates/plugins/fields/view/view-basictext.vm"/>
            <resource name="edit" type="velocity" location="/templates/edit.vm"/>
            <resource name="xml" type="velocity" location="templates/plugins/fields/xml/xml-basictext.vm"/>
        </customfield-type>
    </atlassian-plugin>
    
  5. To support internationalization, add the following line in jira-custom-field-example.properties:

    1
    2
    jira-custom-field.name=Admin Editable Text Field
    

Step 5. Build, install, and run your app

In this step you'll install your app and run Jira. You'll log into your Jira instance as an admin and configure visibility for your custom field. 

  1. Open a Terminal and navigate to your project root. 

  2. To start Jira instance, run the following 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. Find the URL for Jira. 
    Your Terminal outputs the location of your local Jira instance, defaulted to localhost:2990/jira.

    1
    2
    [INFO] jira started successfully in 194s at http://localhost:2990/jira
    [INFO] Type Ctrl-D to shutdown gracefully
    [INFO] Type Ctrl-C to exit
    
  4. Open your Jira instance in a browser window.
    We recommend using Google Chrome or Mozilla Firefox for consistency.

  5. Log in with admin/admin credentials.

  6. Go to cog icon > Issues > Custom Fields

  7. Click Add Custom Field, and then Advanced.

  8. Select Admin Editable Text Field from the list and click Next.

    Choose your app
  9. Associate the field with all screens and click Next.

    Associate fields

Step 6. Create a test user and test your custom field type

Now that you've added your custom field, let's put it to the test. To verify your field settings, you'll create a new user without admin permissions. 

  1. From any page in your local Jira instance, click   >  Users Management.

  2. Click Create User and enter details for a test user. 
    Keep it simple and use your own email address.

    Create a test user
  3. Click Projects > Create Project.
    Now you'll be able to create an issue to test your field.

  4. Choose any template and click Next.

  5. Enter a name (for example, "Test") and click Submit.

  6. Click Create Issue and verify you can enter text into your custom field type as an admin.

    Create an issue as an admin
  7. Log out and log in with your test user credentials.

  8. Create an issue as your test user. 
    Verify that you're unable to modify the field you customized. 

    Create issue as a non-admin

Notes

  • Additional security precautions (since 9.0.0)

    Before Jira 9.0.0, we allowed all types of data in custom fields. This turned out to be a security vulnerability. That's why, starting from Jira 9.0.0, every data type used to store the value of a custom field has to be included in your allowlist. The default allowlist already contains the most often used types of data, so in most cases its extension won't be necessary. If the data type you wanted to use isn't on the allowlist, you should see a warning in the Jira log file: Deserialization of 'your.new.type' was blocked. Consider adding it to the allowlist..

    Extending your allowlist is a two-step process. First, create an implementation of com.atlassian.jira.security.serialization.XmlPluginAllowlistProvider and inside it, return necessary data types from the getAllowlistedClasses method. For example, if the type is com.atlassian.diff.CharacterChunk:

    1
    2
    package com.atlassian.tutorial.jira.customfields;
    
    import com.atlassian.diff.CharacterChunk;
    import com.atlassian.jira.security.serialization.XmlPluginAllowlistProvider;
    
    import javax.validation.constraints.NotNull;
    import java.util.HashSet;
    import java.util.Set;
    
    public class AllowlistProvider implements XmlPluginAllowlistProvider {
        @Override
        public @NotNull Set<String> getAllowlistedClasses() {
            Set<String> set = new HashSet<>();
            set.add(CharacterChunk.class.getName());
            return set;
        }
    }
    

    Secondly, you need to add the implementation as a module to your atlassian-plugin.xml file. The name of the module should be xml-plugin-allowlist. It needs two attributes: key (should be a unique string) and class (should be the name of the class). In this case, it would look like this:

    1
    2
    <xml-plugin-allowlist key="my.custom.allowlist.extension" class="com.atlassian.tutorial.jira.customfields.AllowlistProvider"/>
    

Next steps

To find out more about custom fields, check the following pages:

Rate this page: