JIRA 4.0 : Customizing Custom Field Types for JIRA 3.0
This page last changed on Jun 01, 2005 by mark@atlassian.com.
JIRA 3.0 now allows you to create your own Custom Field Types through the plugin interface. We'll take a look at a few simple examples and explain how you can easily create your own custom field types. Before you start, you may also want to look at the article JIRA Plugin Guide and Vincent Massol's excellent tutorial on writing JIRA 3 plugins. A Quick Custom Field Types PrimerThere's a few things you need to understand before diving into custom fields. A custom field type can have three components.
A custom field class extends the interface CustomFieldType. This interface provides methods to retrieve and store custom fields values. There are several extension points that are available to make creating new custom field types easier (e.g. AbstractSingleFieldType, AbstractMultiSettableCFType). It is also possible to extend existing custom field types to add functionality (e.g.. Currency type extending NumberCFType). The second component are the resource templates which renders the custom field. There are four view types available, each representing a different context to render the custom field.
Linking the Java code and rendering views are the plugin-module descriptors in your atlassian-plugin.xml. They allow JIRA to recognise what custom fields are available to the system and how to render them. Example module descriptor <atlassian-plugin key="com.atlassian.jira.plugin.customfield.example" name="JIRA Customfields Examples Plugin"> <plugin-info> <description>Customfields Examples Plugin.</description> <version>1.0</version> <application-version min="3.0" max="3.0"/> <vendor name="Atlassian Software Systems Pty Ltd" url="http://www.atlassian.com"/> </plugin-info> <customfield-type key="textarea" name="Free Text Field (unlimited text)" class="com.atlassian.jira.issue.customfields.impl.TextAreaCFType"> <description>A multiline text area custom field to allow input of longer text strings.</description> <resource type="velocity" name="view" location="templates/plugins/fields/view/view-basictext.vm"/> <resource type="velocity" name="column-view" location="templates/plugins/fields/view/view-limited-text.vm"/> <resource type="velocity" name="edit" location="templates/plugins/fields/edit/edit-textarea.vm"/> <resource type="velocity" name="xml" location="templates/plugins/fields/xml/xml-basictext.vm"/> </customfield-type> </atlassian-plugin> You can also take a look at the default custom fields that shipped with JIRA 3.0 here. Setting up your Custom Fields plugin projectYou can choose to develop your plugins however you wish. However, we'd recommend the following using Maven 1.0 and the provided skeleton project plugin. Once this has been setup, you only need to run the command maven jar Configuring your project for MavenYou will first need to download and install Maven. Maven is an ant-like build tool that allows downloads any specified project dependencies automatically (just one of the many features). We have provided a project skeleton that will work with Maven (and is used for the example). You should see three files after unpacking the archive.
Maven uses the project.xml to build your project. Some common JIRA dependencies have been already added. Update this with your relevant project information and put any other dependencies that your plugin require here. See the Maven documentation for more on the project descriptor. If you access the Internet through a proxy, you'll also need to configure the project.properties file. Lastly, the atlassian-plugin.xml configures your plugin for JIRA. See A brief guide to JIRA plugins for more details. Maven also provides plugins to help you setup your projects in popular IDEs such as Eclipse and IDEA. We'd recommend using this if you're planning to use either of the two editors. Building and Deploying your Custom Field TypesTo build using Maven run: maven jar in root directory of your project. Any dependencies will be downloaded and and a jar created in the target directory. To deploy, copy the JAR to the lib folder of your JIRA instance. If you have extra libraries that's required beyong what is available in JIRA, you'll need to copy them over as well. Restart your server and your custom fields will be there ready and waiting! You can download the src and compile the examples below in the same way. Admin-only editable fieldFor the first example, we'll construct a custom field that is only be editable by JIRA administrators and appear as a plain text to others. This is a simple customisation of the shipped TextCFType custom field and can be done by change one edit template. First, we need to add the module to the atlassian-plugin.xml. ... <customfield-type key="admintextfield" name="Admin Editable Text Field" class="com.atlassian.jira.issue.customfields.impl.TextCFType"> <description>A text field only editable by JIRA-administrators. Others will see only text.</description> <resource type="velocity" name="view" location="templates/plugins/fields/view/view-basictext.vm"/> <resource type="velocity" name="edit" location="templates/edit-jiraadminonlytext.vm"/> <resource type="velocity" name="xml" location="templates/plugins/fields/xml/xml-basictext.vm"/> </customfield-type> ... A few points:
This module definition exactly matches that of a standard text field except for one line. <resource type="velocity" name="edit" location="templates/edit-jiraadminonlytext.vm"/> We are customizing the edit Velocity template so that it displays as a text box for an administrator but appears as uneditable text for others. Source for edit-jiraadminonlytext.vm is below. edit-jiraadminonlytext.vm #controlHeader ($action $customFieldHelper.id $customFieldHelper.name $customFieldHelper.required $noHeader) #if ($authcontext.user.inGroup('jira-administrators')) <input type="text" name="$customFieldHelper.id" value="$!customFieldHelper.customFieldValue" /> #else #if($customFieldHelper.customFieldValue && !$customFieldHelper.customFieldValue.equals("")) #set ($displayValue = ${customFieldHelper.customFieldValue}) #else #set ($displayValue = 'N/A') #end <span title="This field is editable only by JIRA administrators">$!displayValue</span> <input type="hidden" name="$customFieldHelper.id" value="$!displayValue" /> #end #controlFooter ($action $customFieldHelper.description $noHeader) The above template checks if the user is part of group jira-administrators. If they are, display the text box, else display the value only as uneditable text. There's a few points to note.
And that's it, a new custom field type. Deploy the JAR, login as an administrator and then a normal user and try it out.
Last commented user lookup fieldThe next example deals with a Look-up Custom Field. Lookup fields don't actually store any values. In this case, we want to return the last user who commented on the issue, if they are not an administrator. We only want this field to be visible in the issue navigator and not the edit or view pages. Coding the Custom Field TypeBefore you implement the interface CustomFieldType you should check out the latest javadoc. Since we're only implementing a limited set of functionality (i.e. read-only custom field) many of the methods can be stubbed out. e.g.. ... public void createValue(CustomField field, GenericValue issue, Object value) { // Do nothing } public void updateValue(CustomField field, GenericValue issue, Object value) { // Do nothing } ... The key method used to retrieve the value of our custom field is getValueFromIssue. public Object getValueFromIssue(CustomField field, GenericValue issue) { User currentUser = authenticationContext.getUser(); User lastUser = null; try { List comments = actionManager.getComments(issue, currentUser); if (comments != null && !comments.isEmpty()) { Comment lastComment = (Comment) comments.get(comments.size()-1); User commenter = lastComment.getUser(); if (!commenter.inGroup(JIRA_ADMIN)) { lastUser = commenter; } } } catch (GenericEntityException e) { } return lastUser; } The return type Object is also known as the Transport Object. In this instance it is of type User, but it could be any other type. The Transport type must remain consistent across all methods such as create, update and also the view and edit templates. Wiring it togetherMuch like the previous example, we can reuse some of the the templates that ship with JIRA. <customfield-type key="lastusercommented" name="Last user commenter" class="com.atlassian.jira.plugin.customfield.example.LastUserCommentedCFType"> <description>This is a lookup field that displays the last commenter who is not a JIRA administrator</description> <resource type="velocity" name="column-view" location="templates/plugins/fields/view/view-user.vm"/> <resource type="velocity" name="xml" location="templates/plugins/fields/xml/xml-user.vm"/> </customfield-type> We can omit any resource types that we don't require. Thus both the edit and view templates are omitted here. The field should only appear when viewing through the issue navigator (column-view) and XML/RSS views (xml). The view user adds a link to the user details page and displays the full user name.
Enable SearchingThe last commenter field in itself isn't all that useful. While we can see it in on the issue navigator, we can't search for a particular user who commented it last. Searching in JIRA 3.0 is handled by CustomFieldSearchers. Again several pre-configured searchers are available. You must ensure that the Transport Object are compatible between the custom field and the custom field searcher. Thus we can only use the UserPicker searcher since this is the only one that indexes User objects. <customfield-searcher key="userpickersearcher" name="User Picker Searcher" class="com.atlassian.jira.issue.customfields.searchers.UserPickerSearcher"> <description>Allow to search for a user using a userpicker.</description> <resource type="velocity" name="search" location="templates/plugins/fields/edit-searcher/search-userpicker.vm"/> <resource type="velocity" name="view" location="templates/plugins/fields/view-searcher/view-searcher-basictext.vm"/> <valid-customfield-type package="com.atlassian.jira.plugin.customfield.example" key="lastusercommented"/> </customfield-searcher> This is quite similar to the CustomFieldType definition. The tag valid-customfield-type is used to associate the searcher to any number of custom field types. Package refers to the atlassian-plugin key attribute at the top of a plug-in and and the key refers to the module/customfield key. Now when you create/edit your Last User Commented custom field, you'll be able to select the User Picker as a search template. You can now search on the last commenter field in the issue issue navigator. Important When you change a search template for a custom field, you may need to perform a reindex before the search will work correctly. This issue is being tracked at JRA-4641.
Sorting in Issue NavigatorTo enable sorting you simply need to implement the interface SortableCustomField public class LastUserCommentedCFType extends AbstractCustomFieldType implements SortableCustomField The interface simply extends Comparable, so you need to implement the compare method. public int compare(Object customFieldObjectValue1, Object customFieldObjectValue2) { return new BestNameComparator().compare(customFieldObjectValue1, customFieldObjectValue2); } The BestNameComparator is a simple helper type to facilitate comparing two users. You could just as easily write your own custom compare method. Amazon search pluginLastly, a frivolous plug-in to give you some ideas on how to implement custom fields that perform remote look ups. Basically, we want a custom field that will take a text string and display a results from a search through the Amazon. There are several approaches to this, but by simplest solution is to treat the stored value as a simple text field and then add a viewHelper object that effectively transforms the string into the desired result. Coding and Attaching the view helperFirst we need to code our Amazon view helper. You can take a look in the source, but how it's been implemented isn't all that relevant. Once we have the view helper, we can pass this helper to the Velocity templates through the method getVelocityParameters public Map getVelocityParameters(GenericValue issue) { Map map = new HashMap(); map.put("amazonSearchViewHelper", new AmazonSearchViewHelper()); return map; } The object AmazonSearchViewHelper is now accessible the velocity template. It has the method searchForBooks which returns a list of Books given some key words. We simply invoke this helper method in the template and display the results in a table. #if ($value) Results for search query "${value}" <br /> <table class="grid"> <tr> <th>Title</title> <th>Primary Author</th> </tr> #foreach ($book in $amazonSearchViewHelper.searchForBooks($value)) <tr> <td><a target="_new" href="${book.link}">${book.title}</a</td> <td>${book.firstAuthor}</td> </tr> #end </table> #end You can utilise this same idea to display data from other remote systems, or even combine it with the readonly field to create your very own remote custom field. ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() |
![]() |
Document generated by Confluence on Oct 06, 2009 00:31 |