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. |
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:
GenericTextCFType
for your app.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.
To complete this tutorial, you should:
You should be able to complete this tutorial even if you've never created an app before.
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 2git clone https://bitbucket.org/atlassian_tutorial/jira-custom-field-tutorial.git
Alternatively, you can download the source code for this project as ZIP archive.
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.
Open a Terminal on your machine and navigate to directory where you would like to keep your app code.
To create a Jira app skeleton, run the following command:
1 2atlas-create-jira-plugin
To identify your app, enter the following information.
group-id | com.example.plugins.tutorial.customfields |
artifact-id |
|
version |
|
package |
|
When prompted, confirm your entries with Y
or y
.
Navigate to the jira-custom-field-example
directory created in the previous step.
1 2cd jira-custom-field-example
To delete the test directories, run the following commands:
1 2rm -rf ./src/test/java/ rm -rf ./src/test/resources/
Delete the unnecessary Java files.
1 2rm -rf ./src/main/java/com/example/plugins/tutorial/customfields/*
You'll build a Java class for your app in next steps.
Import the project into your favorite IDE.
JiraCustomField
classYour 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.
JiraCustomField
class that extends GenericTextCFType
with following stub code: 1 2package 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); } }
@JiraImport
and @Scanned
are annotations of Atlassian Spring Scanner.
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.
Under src/main/resources
, create a templates
directory.
1 2mkdir src/main/resources/templates
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".
customfield-type
plugin module to your descriptor fileIn 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 type | Details |
view | Provides a basic read-only view of the field value. |
column-view | Read-only view for displaying in the issue navigator. The issue navigator will display the view value if omitted. |
edit | Renders the edit widget in issue creation, editing issues, and editing defaults. This is the value you'll replace with your custom Velocity template. |
xml | Shows 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.
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.
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
.
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"/>
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>
To support internationalization, add the following line in jira-custom-field-example.properties
:
1 2jira-custom-field.name=Admin Editable Text Field
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.
Open a Terminal and navigate to your project root.
To start Jira instance, run the following command:
1 2atlas-run
This builds your app code, starts a Jira instance, and installs your app. This could take a few minutes.
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
Open your Jira instance in a browser window.
We recommend using Google Chrome or Mozilla Firefox for consistency.
Log in with admin/admin credentials.
Go to > Issues > Custom Fields.
Click Add Custom Field, and then Advanced.
Select Admin Editable Text Field from the list and click Next.
Associate the field with all screens and click Next.
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.
From any page in your local Jira instance, click > Users Management.
Click Create User and enter details for a test user.
Keep it simple and use your own email address.
Click Projects > Create Project.
Now you'll be able to create an issue to test your field.
Choose any template and click Next.
Enter a name (for example, "Test") and click Submit.
Click Create Issue and verify you can enter text into your custom field type as an admin.
Log out and log in with your test user credentials.
Create an issue as your test user.
Verify that you're unable to modify the field you customized.
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 2package 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"/>
To find out more about custom fields, check the following pages:
Rate this page: