Plugin Developer Notes for JIRA 4.4

JIRA 4.4 Upgrade Notes

On this page

Still need help?

The Atlassian Community is here for you.

Ask the community

On this page:

Introduction

JIRA 4.4 introduces several changes that may break existing plugins which are not bundled with JIRA.

If you are using or have been involved in the development of such a plugin, it may need to be updated to work with JIRA 4.4. Please read through the information below to see if any of this content is relevant to your plugin.

If you are using a plugin developed by a third party, please check with the plugin's author to see if the plugin has been tested with JIRA 4.4.

(info) Please Note:

  • This is not the complete list of changes for JIRA 4.4 — it only describes changes in JIRA 4.4 that will impact plugin developers.
  • For details about which versions of Atlassian's Plugin Development Platform and its components (such as the Plugin Framework, Shared Access Layer (SAL), Atlassian User Interface (AUI) and the Atlassian REST Plugin) are included in JIRA 4.4, please refer to Plugin Development Platform Version Matrix​.

PluggableProjectOperation

The PluggableProjectOperation interface allows plugin developers to add links to the project admin section. For example:

These plugin points are now rendered in a different location within JIRA. While the plugins will continue to render they will not look very nice. For example:

To make the plugins fit into the new panel the markup they generate will have to change. The new template looks something like:


<span class="project-config-list-label">Operation for User</span>
<span class="project-config-list-value"><a href="#">User Operation</a></span>

This markup is encapsulated in DefaultPluggableProjectOperation. You can extend this class and provide implementations of the getLabelHtml and getContentHtml methods in your plugin. For example:


public class ExampleOperation extends DefaultPluggableProjectOperation
{
    public boolean showOperation(Project project, User user)
    {
        return true;
    }

    protected String getLabelHtml(Project project, User user)
    {
        return descriptor.getI18nBean().getText("com.example.plugin.projectop.label");
    }

    protected String getContentHtml(Project project, User user)
    {
        return descriptor.getHtml("view", ImmutableMap.of("some", "parameter"));
    }
}

This class should isolate you from small changes to the markup necessary to show your operation on the project summary page.

Formatting and Parsing Dates Using the Appropriate Time Zone

JIRA 4.4 introduces the concepts of user time zone and default user time zone. JIRA will attempt to use the user time zone when displaying dates to a user and similarly when interpreting dates entered by the user. If a user has not specified a time zone in their user profile, JIRA will fall back to the default user time zone, which can be configured by a JIRA administrator.

(Note: Date fields, which have no time component, such as due dates, release dates (associated with versions) and custom date fields, solely record date information (and no time zone-related information), and therefore remain in the default server timezone. If plugins are displaying these values, they might be inconsistent with JIRA otherwise, especially if they use OutlookDate which applies the user timezone.)

In order to provide a consistent user experience, plugins that target JIRA 4.4 should be mindful of the time zone that is in use when parsing and formatting dates. It is not enough to instantiate a java.text.SimpleDateFormat or a org.joda.time.format.DateTimeFormatter class, as these classes will use the default JVM time zone and locale, which may not necessarily match the user's specified time zone in JIRA.

From JIRA 4.4, the recommended way of formatting and parsing dates is to use a com.atlassian.jira.datetime.DateTimeFormatter. Here is a plugin class example that creates a couple of formatters when the plugin is started, for use at a later point in time:


class MyPluginClass
{
    private final DateTimeFormatter defaultFormatter;
    private final DateTimeFormatter userFormatter;

    public MyPluginClass(DateTimeFormatterFactory factory)
    {
        defaultFormatter = factory.formatter().withStyle(DateTimeStyle.DATE);
        userFormatter = defaultFormatter.forLoggedInUser();
    }

    public String getDateInDefaultTimeZoneAndLocale()
    {
        // uses the default JIRA locale and time zone
        return defaultFormatter.format(new Date());
    }

    public String getDateForLoggedInUser()
    {
        // looks up the currently logged in user, and uses his/her time zone and locale
        return userFormatter.format(new Date());
    }
}

Notes About OutlookDate and its Deprecation

The OutlookDate class has been retrofitted to account for the new user time zone and default user time zone concepts. Hence, plugins that use this class to display and parse time zones will automatically take on this new user time zone behaviour. While this is generally the desired behaviour, plugin developers can override the time zone and locale used in the com.atlassian.jira.datetime.DateTimeFormatter class.

OutlookDate has been deprecated and will be removed in a future version of JIRA. We encourage plugin writers to port their plugins over to the new com.atlassian.jira.datetime.DateTimeFormatter API as soon as possible.

Single- and Multi-Select Custom Field Changes

Since the following single- and multi-select custom fields are now editable, you should be aware of the changes below which may affect your plugins.

  • Select List
  • Multi Select
  • Cascading Select
  • Radio Buttons
  • Multi Checkboxes

See JRA-2983 for more information about this JIRA improvement.

Database Changes

There are two database changes that result from this improvement:

  1. We now store the ID (as opposed to the literal value) of the custom field option in the customfieldvalue table.
  2. We have added a 'disabled' flag to the customfieldoption table. When this flag is set to 'true' for a given option, that option is not available for selection when creating or editing an issue. You should honour this behaviour when using or extending your single- and multi-select custom field types. Disabled values are still available for searching.

Effect on Existing Plugins

This improvement will affect plugins that provide custom fields (of type select or multi-select) which reuse or extend the following classes:

  • com.atlassian.jira.issue.customfields.impl.SelectCFType and
  • com.atlassian.jira.issue.customfields.impl.MultiSelectCFType

If your plugin extends these types, you may need to adjust its behaviour. Velocity templates for editing and searching should be adjusted to accept and return the custom field option ID in the value attribute.

If your plugin consumes the output of the com.atlassian.jira.issue.Issue.getCustomFieldValue() you should be aware that this method now returns a com.atlassian.jira.issue.customfields.option.Option object rather than a String for Select or MultiSelect custom fields. You can use the Option.toString() method to make your pugin compatible with both JIRA 4.4 and earlier versions.

Please note:

  • Only select and multi-select custom field types are affected by this change. Any custom fields of these types will continue to operate correctly, as the database will be updated during the upgrade to 4.4. Third party custom field types that extend the select and multi-select custom field types may break, but that depends upon their implementation, for example if they provided different templates for viewing or editing these would need to be updated.
  • Plugins that provide custom field types that extend select and multi-select custom field types, can build a plugin that is compatible with both JIRA 4.3 and JIRA 4.4 but that may not be practical in some cases and it may be easier to have separate versions.

Restrictions on the Alias Names of plugin Webwork Actions

In JIRA 4.4 we have restricted the alias names that you can use for your Webwork actions in a small way. It can't start with 'webwork.'. So for example you cant have an action aliased as 'webwork.MyAction.jspa'.

We did this because for performance reason Webwork calls down to all layers to find webwork properties such as 'webwork.action.prefix' and so on as well as trying to find action aliases. This results in hundreds of calls to plugins for an answer we never intended that they answer. So we have limited the times they will be asked via this string matching rule.

View Issue Content & Project Admin Summary is now Pluggable via Web Panels

We have converted the right-hand side of the View Issue page and the entire Project Admin Summary page to use Web Panels for rendering content. This has enabled us to make this a plugin point for plugin writers.
E.g.

The location for the webpanel needs to be: "atl.jira.view.issue.right.context" for the View Issue page. In JIRA 5.0 we aim to make the left hand side pluggable as well.

(tick) For the Admin Project Summary page use "webpanels.admin.summary.left-panels" or "webpanels.admin.summary.right-panels".

Here is how the Time Tracking block is created:


<!-- Time tracking web panel -->
    <web-panel key="timetrackingmodule" location="atl.jira.view.issue.right.context" weight="60">
        <context-provider class="com.atlassian.jira.plugin.viewissue.TimeTrackingViewIssueContextProvider"/>
        <resource name="view" type="velocity" location="timetracking/timetracking.vm"/>

        <label key="timetracking.title" />
        <condition class="com.atlassian.jira.plugin.webfragment.conditions.IsFieldHiddenCondition" invert="true">
            <param name="field">timetracking</param>
        </condition>
        <condition class="com.atlassian.jira.plugin.webfragment.conditions.TimeTrackingEnabledCondition"/>
    </web-panel>

In this example:

  • The context provider, TimeTrackingViewIssueContextProvider, populates the context for the velocity template.
  • The velocity template, timetracking.vm, is responsible for rendering the block.
  • The label provides the label for the block.
  • The conditions are evaluated to determine if we should show the block.

The Web Panel is responsible for providing the content inside the "module" chrome. The module chrome provides the block heading (from the Web Panel label), the collapsable states and further plugin points for the header. If you do not wish to have this chrome rendered for you (e.g. you may just want to include some javascript on the page), you need to specify the following inside your module descriptor:


<param name="headless">true</param>

This will just put the exact output of your Web Panel into the page.

The additional plugin points the common chrome provides are:

Adding action icons to the header

These are actually just styled up Web Items.

The location is: "<web-panel-full-key>/header". 

Here is how we render the "Add Attachment icon":


<!--  Add Attachment link -->
    <web-item key="add-attachments-link" i18n-name-key="webfragments.view.issue.attachments.create" name="Create an Attachments" section="com.atlassian.jira.jira-view-issue-plugin:attachmentmodule/header" weight="1">
        <label key="admin.issue.operations.plugin.attach.file.name"/>
        <tooltip key="admin.issue.operations.plugin.attach.file.name"/>
        <styleClass>issueaction-attach-file icon icon-add16</styleClass>
        <link linkId="add-attachments-link">
            /secure/AttachFile!default.jspa?id=${issue.id}
        </link>
        <condition class="com.atlassian.jira.plugin.webfragment.conditions.CanAttachFileToIssueCondition"/>
    </web-item>

Note the styleClass in this example as it is responsible for styling the link as an icon.

Adding links to the dropdown.

You can also add Web Items to the dropdown of a block.

The default location of these are: "<web-panel-full-key>/drop/default".
E.g. "com.atlassian.jira.jira-view-issue-plugin:attachmentmodule/drop/default"

Adding sections to the dropdown

You can also define Web Sections for the dropdown, to group Web Items together.

The location for these are: "<web-panel-full-key>/drop".
E.g. "com.atlassian.jira.jira-view-issue-plugin:attachmentmodule/drop"
To add Web Items to these sections, use the location: "<web-panel-full-key>/drop/<section-key>".
E.g. "com.atlassian.jira.jira-view-issue-plugin:attachmentmodule/drop/attachment-sorting-options"

Web-fragments and the administration navigation changes

Traditionally navigation in the administration part of JIRA was done in a huge list of links down the left hand side of the page. We have moved to having an administration mode with menus at the top of the page. These menus follow a new, simpler way to generate themselves and web-items and web-sections in existing plugins will need to be updated to fit with the new structure. Unchanged web-items will still show in the menus, all together at the bottom of the "Plugins" menu.

The new structure follows the rules:

  • Any level of web-section can have both or either web-items and web-sections in it.
  • The location attribute for a web-section is the web-section it is inside of.
  • The section attribute of a web-item is the locationOfTheSection/section.

The new structure has the new location of system.admin.top.navigation.bar and a section for each menu at that location. There are 2nd level web-sections for each menu under that. 3rd level web-sections for each section within a menu and 4th level web-sections so that a set of web-items that will appear as tabs at the side will appear as a single item in the top level menus, this menu item will link to the web-item with the lowest weight in that web-section.

It is strongly recommended to put all plugins web-sections and web-items in the Plugin menu (location admin_plugins_menu) and we have created some predefined sections to put you to use:

Section Name

web-item's section attribute

web-section's location attribute

Source Control

admin_plugins_menu/source_control

source_control

Builds

admin_plugins_menu/builds_section

builds_section

Agile

admin_plugins_menu/agile_section

agile_section

Testing

admin_plugins_menu/testing_section

testing_section

Requirements

admin_plugins_menu/requirements_section

requirements_section

Timetracking

admin_plugins_menu/timetracking_section

timetracking_section

Integrations

admin_plugins_menu/integrations_section

integrations_section

Workflow

admin_plugins_menu/workflow_section

workflow_section

Drawing

admin_plugins_menu/drawing_section

drawing_section

 Adding web-items and sections to these new sections means they will also be rendered in the administration summary page.

The Source Control and Builds sections of the Plugins menu are shown below:

And example of adding a web-item for Bamboo Configuration to the Builds section would be:


    <web-item key="bambooConfigLink" name="Bamboo Config Web Item" section="admin_plugins_menu/builds_section" weight="10">
        <condition class="com.atlassian.jira.plugin.webfragment.conditions.JiraGlobalPermissionCondition">
            <param name="permission">admin</param>
        </condition>
        <label key="bamboo.config.title"/>
        <link linkId="bamboo_config">/secure/admin/jira/ViewBambooApplicationLinks.jspa</link>
    </web-item>

If you want to keep your plugin compatible with versions of JIRA prior to 4.4, you can add a condition to your existing web-item for older versions of JIRA so that it will only show up for them. For example in the Bamboo Plugin we have a web-item:


<web-item key="bambooConfigLink-old" name="Bamboo Config Web Item" section="system.admin/globalsettings" weight="19">
   <condition class="com.atlassian.jira.plugin.webfragment.conditions.JiraGlobalPermissionCondition">
      <param name="permission">admin</param>
   </condition>
   <condition class="com.atlassian.jira.plugin.ext.bamboo.conditions.IsPriorToJiraVersion">
      <param name="majorVersion">4</param>
      <param name="minorVersion">4</param>
   </condition>
   <label key="bamboo.config.title"/>
   <link linkId="bamboo_config">/secure/admin/jira/ViewBambooApplicationLinks.jspa</link>
</web-item>

With the IsPriorToJiraVersion is:


public class IsPriorToJiraVersion implements Condition {

    private int maxMajorVersion;
    private int maxMinorVersion;
    private int majorVersion;
    private int minorVersion;

    public IsPriorToJiraVersion(ApplicationProperties applicationProperties) {
        String versionString = applicationProperties.getVersion();
        String versionRegex = "^(\\d+)\\.(\\d+)";
        Pattern versionPattern = Pattern.compile(versionRegex);
        Matcher versionMatcher = versionPattern.matcher(versionString);
        versionMatcher.find();
        majorVersion = Integer.decode(versionMatcher.group(1));
        minorVersion = Integer.decode(versionMatcher.group(2));
    }

    public void init(final Map<String, String> paramMap) throws PluginParseException {
        maxMajorVersion = Integer.decode(paramMap.get("majorVersion"));
        maxMinorVersion = Integer.decode(paramMap.get("minorVersion"));
    }

    public boolean shouldDisplay(final Map<String, Object> context) {
        return (majorVersion < maxMajorVersion) || (majorVersion == maxMajorVersion) && (minorVersion < maxMinorVersion);
    }
}

Tab navigation in admin section

To ensure that the new admin decorator can highlight the correct dropdown in the header and render the correct tabs on the left hand side, the page being decorated needs to tell the decorator which admin section it belongs to. This is done through the use of <meta/> tags.

For example the JIRA FishEye plugin provides three admin web-items to be rendered under the 'Source Control' section:

In order for this to work correctly, the page source for the FishEye Configuration page has to include the following <meta/> tags to tell the new admin decorator which tabs to render:


<meta name="admin.active.section" content="admin_plugins_menu/source_control">
<meta name="admin.active.tab" content="fisheye_config">

These meta tags will have to be included on every one of your admin pages and will have to refer back to the web-items you defined earlier.

Plugging in to the new Project Administration 

This is fairly similar to the integrating with the new project tab look and feel.

To add content to the Summary tab, see above.

Instead it is a simple web-section//web-item, for example:

<web-section key="yoursection" name="Your Config Group" location="atl.jira.proj.config" i18n-name-key="ayoukey" weight="50"/>

Or you can use the inbuilt sections - projectgroup1, projectgroup2, projectgroup3, projectgroup4
Then add a web-item to it.

<web-item key="view_project_your_tab" name="Your Tab" section="atl.jira.proj.config/yoursection" i18n-name-key="yourkey" weight="10">
  <label key="your_label" />
  <link linkId="view_project_your_tab">a_link_to_your_content</link>
</web-item>

That is enough to add your tab.

To then wrap your content with the project config decorator and have the right tab selected, your content should have the following in its header:

<head>
  <title>$title</title>
  <meta name="decorator" content="admin"/>
  <meta name="projectKey" content="$project.key"/>
  <meta name="projectId" content="$project.id"/>
  <meta name="admin.active.tab" content="id_of_your_tab"/>
  <meta name="admin.active.section" content="atl.jira.proj.config"/>
</head>

First create a tab group (web-section) you want to add to:

<web-section key="your_section_key" name="Your Config Group" location="atl.jira.proj.config" i18n-name-key="your.i18n.key" weight="50"/>

or you can use an existing section (weights are in brackets after sections & items):

  • projectgroup1 (10) - Contains Summary(10)
  • projectgroup2 (20) - Contains Issue Types(10), Workflows(20), Screens(30) & Fields(40)
  • projectgroup3 (30) - Contains People(10), Permissions(20), Issue Security(30) & Notifications(40)
  • projectgroup4 (40) - Contains Versions(10) & Components(20)

Add a web-item to your section.

<web-item key="your_tab_key" name="Your Tab" section="atl.jira.proj.config/your_section_key" i18n-name-key="your.i18n.key" weight="10">
  <label key="your_label" />
  <link linkId="view_project_your_tab">a_link_to_your_content</link>
</web-item>

Now you will have a tab in Project Configuration. The link should point to some content.  This can be provided by a WebWork Action, REST resource or Servlet.

To then wrap your content with the project config decorator and have the right tab selected, your content should have the following in its header:

<head>
    <title>Your Page Title</title>
    <meta name="decorator" content="admin"/>
    <meta name="projectKey" content="key_of_active_project"/>
    <meta name="projectId" content="id_of_active_project"/>
    <meta name="admin.active.tab" content="view_project_your_tab"/>
    <meta name="admin.active.section" content="atl.jira.proj.config"/>
</head>

These meta tags will have to be included on every one of your project admin pages

Version-related Atlassian Events

The following new Atlassian Events are available in JIRA 4.4:

  • VersionMergeEvent
  • VersionDeleteEvent
  • VersionCreateEvent
  • VersionReleaseEvent
  • VersionUnreleaseEvent
  • VersionArchiveEvent
  • VersionUnarchiveEvent
  • VersionMoveEvent

See JIRA-specific Atlassian Events for a complete list.

Gadget Web-Resources

As of JIRA 4.4 gadgets should no longer depend on the com.atlassian.jira.gadgets:common web resource. This change was introduced in JIRA 4.3, however due to some changes to the gadgets framework in JIRA 4.4 gadgets still depending on this web-resource will now break.

Instead gadgets should only need to rely on the more lightweight com.atlassian.jira.gadgets:common-lite web resource.

For more information, please refer to JRA-25039.

As of JIRA 4.4, all plugin modules are now dynamically reloadable

Traditionally some of the modules were bearing the @RequiresRestart annotation, which indicated that installing plugins containing such modules required a full restart of JIRA. As of JIRA 4.4 each plugin module is capable of being dynamically added and removed from the running JIRA instance. This has some important implications for the plugin developers, encapsulated by the following recommendations:

  • avoid using static caches or statically accessible components (the singleton pattern) in your plugins - use component module type instead
  • avoid manipulating / storing class loaders and threads in your plugin components - this may potentially lead to memory leaks given your plugin is installed and taken down from running JIRA instance multiple times
  • the following life-cycle interfaces are available for your components to implement and will be called upon relevant plugin life cycle events:
    • org.springframework.beans.factory.InitializingBean - its afterPropertiesSet() method will be called each time the Spring context for given plugin is created, i.e. each time the plugin is enabled. NOTE this method is called immediately after given component instance is created and fully initialized, and not after the plugin has been fully enabled. In fact, when the whole plugin system is starting (as opposed to just your plugin being enabled), this is likely to be called before the plugin system has been initialized. Therefore you cannot rely on the plugin system state when implementing this interface. The preferred way in such case is to listen to Atlassian plugin framework events and use this method to only to register itself with the EventPublisher
    • org.springframework.beans.factory.DisposableBean - its destroy() method will be called when the component is being destroy as its Spring context is being removed. Similarly to the InitializingBean, implementing classes cannot rely on the state of the plugin system (e.g. calling other components within destroy() may cause exceptions as those components may have already be removed from the destroyed context). Please use this method sparingly, e.g. to unregister the component from the EventPublisher, and otherwise use the plugin framework events instead
    • com.atlassian.sal.api.lifecycle.LifecycleAware (see javadoc) - called when the plugin system starts up
  • avoid implementing com.atlassian.jira.extension.Startable in you plugins, it is meant to be used by internal JIRA components only and its support for plugins will soon be removed.

REST API Changes in JIRA 4.4 (from JIRA 4.3)

Retrieving a List of Groups

You can now retrieve a list of all groups in a JIRA installation, as well as a filtered list of groups matching a specified 'query' substring using the following HTTP GET action on:

  • http://hostname/rest/api/2.0.alpha1/groups/picker

For example, http://hostname/rest/api/2.0.alpha1/groups/picker?query=admin will retrieve a list of groups containing the 'admin' substring. Refer to the REST API documentation for more information.

Deleting a Watcher from an Issue

To delete a watcher from an issue, the REST API call format has changed.

For example, to delete a user with username 'fred' from issue 'PROJ-123', you would use the following formats:

  • In JIRA 4.3 (and earlier) — http://hostname/rest/api/2.0.alpha1/issue/PROJ-123/watchers/fred
  • In JIRA 4.4 (and later) — http://hostname/rest/api/2.0.alpha1/issue/PROJ-123/watchers?fred

Refer to the REST API documentation for more information.

Create, Read, Update or Delete Project Components

You can create, read, update or delete project components using the following REST calls:

Action

REST API call

Create a project component

HTTP POST on http://hostname/rest/api/2.0.alpha1/component http://docs.atlassian.com/jira/REST/4.4/#id2475780

Read/get a project component along with the total number of issues with that component

HTTP GET on http://hostname/rest/api/2.0.alpha1/component/{id}/relatedIssueCounts http://docs.atlassian.com/jira/REST/4.4/#id2475988

Read/get a detailed list of information about a project component

HTTP GET on http://hostname/rest/api/2.0.alpha1/component/{id} http://docs.atlassian.com/jira/REST/4.4/#id2475844

Modify a project component

HTTP PUT on http://hostname/rest/api/2.0.alpha1/component/{id} http://docs.atlassian.com/jira/REST/4.4/#id2475844

Delete a project component

HTTP DELETE on http://hostname/rest/api/2.0.alpha1/component/{id}?moveIssuesTo http://docs.atlassian.com/jira/REST/4.4/#id2475844
(You can assign any issues associated with the project component being deleted (i.e. {id}) to another component specified as the value of the moveIssuesTo parameter.)

Create, Read, Update or Delete Project Versions

You can create, read, update or delete project versions using the following REST calls:

Action

REST API call

Create a project version

HTTP POST on http://hostname/rest/api/2.0.alpha1/version http://docs.atlassian.com/jira/REST/4.4/#id2474955

Read/get a list of information about a project version

HTTP GET on http://hostname/rest/api/2.0.alpha1/version/{id} http://docs.atlassian.com/jira/REST/4.4/#id2475026

Read/get a project version along with the total number of issues fixed and affected in that version

HTTP GET on http://hostname/rest/api/2.0.alpha1/version/{id}/relatedIssueCounts http://docs.atlassian.com/jira/REST/4.4/#id2475180

Read/get a project version along with the total number of unresolved issues in that version

HTTP GET on http://hostname/rest/api/2.0.alpha1/version/{id}/unresolvedIssueCount http://docs.atlassian.com/jira/REST/4.4/#id2475234

Modify a project version's sequence within the project

HTTP PUT on http://hostname/rest/api/2.0.alpha1/component/{id}/move http://docs.atlassian.com/jira/REST/4.4/#id2475287

Delete a project version

HTTP DELETE on http://hostname/rest/api/2.0.alpha1/version/{id} http://docs.atlassian.com/jira/REST/4.4/#id2475026

Date Format Improvements

Due dates and custom field dates are now only parsed/presented in a simple year, month and day format. Additional timezone-specific content is no longer required, nor expected.

Last modified on Nov 16, 2011

Was this helpful?

Yes
No
Provide feedback about this article
Powered by Confluence and Scroll Viewport.