Last updated Feb 15, 2024

Displaying content in a dialog in Jira

Level of experience:

Intermediate

Time to Complete:

Approximately 1 hour 30 minutes

Overview of the tutorial

This tutorial shows you how to display a dialog with content from the server and have it interact with your page. In this tutorial, you'll develop a dialog that sets the version in which an issue should be fixed.

This tutorial adds dialog functionality to already existing app that has:

  • A Webwork action for that contains the logic for scheduling an issue.
  • A Velocity template for rendering the form.
  • A test for ensuring that the scheduling works.

To display this form in a dialog and have it interact with the page, we'll go through a few different phases.

Simplest Dialog

Adds a JavaScript resource to "slurp" the form content into a dialog using generic methods.

Standard Dialog

Create a dialog for this specific use case that will work in all circumstances.

Advanced Dialog

We'll update the underlying page so that we don't need to do a page refresh.

App source

We encourage you to work through this tutorial. If you want to skip ahead or check your work when you are done, you can find the app source code on Atlassian Bitbucket. Bitbucket serves a public Git repository containing the tutorial's code. To clone the repository, issue the following command to get the completed tutorial code:

1
2
git clone https://bitbucket.org/atlassian_tutorial/jira-scheduler-dialog-complete.git

Alternatively, you can download the source using the get source option here: https://bitbucket.org/atlassian_tutorial/tutorial-jira-scheduler.

Required knowledge

You should already have installed the following software:

  • An integrated development environment(IDE) such as Eclipse or IDEA.
  • Atlassian Plugin SDK 3.7 or higher.
  • Git distributed source control system

You should also understand the Java development basics including classes, interfaces, methods, how to compile source into a JAR file, and so on.

About these instructions

You can use any supported combination of OS and IDE to construct this app. These instructions were written using Eclipse Classic Version 3.7.1 on a MacBook Air running Mac OS X Version 10.7.2. If you're using another combination, you should use the equivalent operations for your specific environment.

Step 1. Getting the base app code

  1. Change directory to your Eclipse workspace.

  2. Download the base tutorial source.

    1
    2
    $ git clone https://bitbucket.org/atlassian_tutorial/jira-scheduler-dialog-base.git
    
  3. Change directory to the app's root directory (where the pom.xml files is located).

  4. Run some the tests to ensure that the starting point is working.

    1
    2
    $ atlas-integration-test
    

    The command builds the code and runs the integration tests that come with the source. Throughout this tutorial, you'll run a number of atlas-commands that automate much of the work of app development for you. If everything run successfully, you should see a message similar to:

    1
    2
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESSFUL
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time: 2 minutes 19 seconds
    [INFO] Finished at: Sat Nov 26 13:26:03 EST 2011
    [INFO] Final Memory: 70M/123M
    [INFO] ------------------------------------------------------------------------
    

Step 2. Import the code into Eclipse

  1. Change to the tutorial-jira-scheduler directory created by the previous step.

  2. Run the command:

    1
    2
    atlas-mvn eclipse:eclipse
    
  3. Start Eclipse.

  4. Select File > Import.
    Eclipse starts the Import wizard.

  5. Filter for Existing Projects into Workspace (or expand the General folder tree).

  6. Select Next and enter the root directory of your workspace.
    Your Atlassian app folder should appear under Projects.

  7. Select your app and select Finish.
    Eclipse imports your project.

Step 3. Verify your Jira version

The source files were compiled against the Jira version that existed when the code was created. The version designation is in the project pom.xml file (Project Object Model definition file). This file is located in the project project and declares the project dependencies. Take a moment and examine the Jira dependency:

  1. Open the pom.xml file.

  2. Scroll to the bottom of the file.

  3. Find for the <properties> element.
    This section lists the version of the Jira version you selected in Step 1 and also the version of the atlas- commands you are running.

  4. Enter the Jira version number in the <jira.version> element. Below, you can see an example for Jira 5.2.

    1
    2
    <properties>
        <jira.version>5.2</jira.version>
        <amps.version>3.7</amps.version>
    </properties>
    
  5. Save the pom.xml file.

Step 4. Update your project and refresh your IDE

If you change your Atlassian project, Eclipse isn't automatically aware of the changes. Moreover, sometimes your project dependencies require an update. We need to fix that.

  1. Switch to a terminal window.

  2. Change directory to the project root.
    This is the directory with the pom.xml file.

  3. Update the on-disk project metadata with the new POM information.

    1
    2
    atlas-mvn eclipse:eclipse
    
  4. Back in Eclipse, refresh the app project to pickup the changes.

Remember to do this update and refresh step each time you edit your pom.xml or otherwise modify your app source with an Atlassian command.

Step 5. Test the base app in Jira

At this point, you haven't actually written any Java code. You can however run Jira and see the base app in action. In this step, you'll start Jira, create a project you'll use later, and test the servlet.

  1. Open a terminal window and navigate to the app root folder (where the pom.xml file is).

  2. Run the following command:

    1
    2
    atlas-run
    

    This command builds your app code, starts a Jira instance, and installs your app in it. This may take several seconds. When the process completes, you'll see many status lines on your screen concluding with something like the following lines:

    1
    2
    [INFO] jira started successfully in 71s at http://localhost:2990/jira
    [INFO] Type CTRL-D to shutdown gracefully
    [INFO] Type CTRL-C to exit
    
  3. Open your browser and navigate to the local Jira instance started by atlas-run.
    If you followed the instructions, enter http://localhost:2990/jira in your browser.

  4. At the Jira login, enter a username of admin and a password of admin.
    The integration steps you ran earlier created a project called TEST in your Jira instance. You should have an existing issue called TEST-1 in the Recent Issues category.

  5. Display the TEST-1 issue.
    Between the Edit and Assign button, you should see the Schedule button. It launches the app.

  6. Experiment with the issue creation and notice how the button and the resulting dialog behave.

Now, we're ready to start writing some code!

Step 6. Creating the simplest of dialogs

Jira has a feature that allows you to simply add a class to a link in HTML to open the contents of the target page in a dialog. Start Eclipse and do the following:

  1. Edit the atlassian-plugin.xml file.

  2. Locate the <web-item> with a key value of schedule-web-item.

  3. Add the following styleClasselement as a child element.

    1
    2
    <styleClass>trigger-dialog</styleClass>
    

    When you finish, the <web-item appears as follows:

    1
    2
    <web-item name="Schedule Web Item" i18n-name-key="schedule-web-item.name" key="schedule-web-item" section="operations-top-level" weight="3">
    <description key="schedule-web-item.description">The Schedule Web Item Plugin</description>
    <conditions type="and">
        <condition class="com.atlassian.jira.plugin.webfragment.conditions.IsIssueEditableCondition"/>
        <condition class="com.atlassian.jira.plugin.webfragment.conditions.HasIssuePermissionCondition">
        <param name="permission" value="edit"/>
        </condition>
        <condition class="com.atlassian.jira.plugin.webfragment.conditions.IsFieldHiddenCondition" invert="true">
        <param name="field" value="fixVersions"/>
        </condition>
    </conditions>
    <label key="schedule-web-item.label"/>
    <link linkId="schedule-web-item-link">/secure/SchedulerWebworkModuleAction!default.jspa?id=${issue.id}</link>
    <styleClass>trigger-dialog</styleClass>
      </web-item>
    
  4. Save your changes.

  5. Return to a terminal window and navigate to the app directory.

  6. Enter atlas-debug at the prompt.
    Starting in the debug mode will allow you to use the Fastdev technique.

  7. Open your Jira test instance in a browser and log in.

  8. Open an existing issue (you should have at least one).

  9. Test the Schedule button's behavior.
    Selecting the button will bring up the Schedule dialog. When you select Schedule within this dialog, the content of the dialog is submitted to the system.

"Not seeing the Schedule button?"

Ensure the following:

  • There are issues in the system.

  • There are versions in the project your issue is in.

  • You don't have permission to edit the issue.

Keep Jira up and running in your active browser. From this point forward, we'll use the Fastdev approach to test our code changes.

Step 7. Cleaning up the Submit action

In the previous step, you saw that submitting the form caused the interface to look jumbled. We need to make the server tell the dialog that it can close. There is a convenient method in Webwork Actions that allows you to do this.

  1. Open Eclipse.

  2. Locate the SchedulerWebworkModuleAction.java file and open it for editing.

  3. Change the doExecute() method in the class so that it looks returns the following:

    1
    2
    /**
        * The business logic of your form.
        * Only gets called if validation passes.
        *
        * @return the view to display - should usually be "success"
        */
    @RequiresXsrfCheck
    protected String doExecute() throws Exception
    {
        // Business Logic
        final IssueService.IssueResult update = issueService.update(authenticationContext.getLoggedInUser(), updateValidationResult);
    
        if (!update.isValid())
        {
            return ERROR;
        }
    
        // We want to redirect back to the view issue page so
        return returnComplete("/browse/" + update.getIssue().getKey());
    }
    

    If you are following the tutorial, you should still have Jira up and and running.

  4. Go to the browser page and a hard refresh of the page. To do this, you can:

    • Shift+Reload in most browsers.
    • Ctrl+Reload on Windows or in Internet Explorer.
    • In Safari 5, you will need to hold down the Shift key while clicking the Reload icon in the Location bar.
      Reloading the app should have it working correctly with the view issue page reloading after the dialog closes.

Step 8. Learn what is happening behind the scenes

This section discusses what happens behind the scenes in the browser and on the server.

In the browser

On page load, the app overrides the link's click behavior. We specified this when we added the <styleClass>trigger-dialog</styleClass>. Instead of following the link, it loads the target page via Ajax and puts the contents in a dialog. We add some URL parameters to the links target to allow the server to know the contents are being rendered in a dialog and they can customize the content. The URL parameters are:

inline=true

Specifies the content will open in a dialog.

decorator=dialog

Ensures the content uses the dialog Sitemesh decorator instead of it's usual one.

The result of any form submision or the target of any links is put into this dialog until the server returns a special response header: X-Atlassian-Dialog-Control:DONE. Any response header that contains this will close the dialog and refresh the underlying page.

On the server

The returnComplete(url) checks to see if the content is displayed in a dialog. If it is, the system adds the header and returns an empty response. If it is not, it will behave as usual.

You can also redirect to another page when dialog is finished. To do this, instead of closing the dialog and refreshing the current page, you can redirect to another page when the dialog closes. To do this, return X-Atlassian-Dialog-Control:redirect:<url-to-redirect-to> instead of X-Atlassian-Dialog-Control:DONE.

To do this in a Webwork Action, edit the SchedulerWebworkModuleAction class to use returnCompleteWithInlineRedirect(<url-to-redirect-to>) instead of calling returnComplete(...).

Limitations of the simplest dialog

When using the simplest dialog, you won't be able to customize behavior on dialog close.

Step 9. Creating a standard dialog

For our dialog to work every time, we will need to create a specialized version of a dialog.
In this section, we'll add some JavaScript onto the page that will instantiate the form and bind it to the link. Additionally, we'll use the app module generator, part of the Atlassian Plugin SDK, to generate the stub for our web-resource.

  1. Open a command window and go to the app root folder (where the pom.xml is located).

  2. Run atlas-create-jira-plugin-module.

  3. Supply the following information as prompted:

    Choose Plugin Module:

    Web Resource

    Enter Plugin Module Name:

    My Web Resource

    Enter Resource Name:

    scheduler.js

    Enter Resource Type:

    download

    Enter Location:

    javascript/scheduler.js

    Add Resource Parameter?:

    Y

    param name:

    content-type

    param value:

    text/javascript

    Add Resource Parameter?:

    N

    Add Resource:

    N

    Show Advanced Setup?:

    N

    Add Another Plugin Module?:

    N

    This will modify your atlassian-plugin.xml file to include a Web Resource.

  4. In Eclipse, create a folder called javascript in src/main/resources.

  5. Add a scheduler.js file in your new javascript folder.

  6. Edit scheduler.jsand add the following form:

    1
    2
    AJS.$(function () {
        Jira.Dialogs.scheduleIssue = new Jira.FormDialog({
            id: "schedule-dialog",
            trigger: ".issueaction-schedule-issue",
            ajaxOptions: Jira.Dialogs.getDefaultAjaxOptions,
            onSuccessfulSubmit : Jira.Dialogs.storeCurrentIssueIdOnSucessfulSubmit,
            issueMsg : 'thanks_issue_updated'
        });
    });
    
  7. Save the file.

  8. Edit the atlassian-plugin.xml file.

  9. Change the styleClass element value from trigger-dialog to issueaction-schedule-issue as follows:

    1
    2
    <styleClass>issueaction-schedule-issue</styleClass>
    
  10. Add a context element to the new web-resource element (to allow the new dialog to be visible from all non-administrative pages) as follows:

    1
    2
    <context>atl.general</context>
    
  11. Save the file.

  12. In eclipse, locate the SchedulerWebworkModuleAction.java file again and open it for editing.

  13. Add a requireResource() call in the includeResources() method to register the new scheduler resource:

    1
    2
    private void includeResources() {
        webResourceManager.requireResource("jira.webresources:jira-fields");
        webResourceManager.requireResource("com.example.plugins.tutorial.tutorial-jira-scheduler:my-web-resource");
    }
    
  14. Save the file.

Step 10. Learn about the JavaScript and test again

Take a minute to examine the file in you examine the new a scheduler.js file you created in the last step. This code creates a form dialog by specifying the following:

Jira

This is the dialog instance. Any dialogs that live in the namespace Jira.DIALOGS automatically get bound to dynamic issue content.

new Jira.FormDialog()

The standard form constructor this takes parameters to change it's behavior.

id

A unique DOM id for the dialog.

trigger

This is what the dialog is bound to. This is usually a jQuery selector.

ajaxOptions

Options that passed to the jQuery.ajax() method and used to retrieve content for the dialog.

Jira

are the defaults.

onSuccessfulSubmit

The function called after a form was submitted and come back successful.

Jira

Stores the current issue for later use in the message displayed on page refresh.

Test out your new code. Reload the app and this form should now work from not only the View Issue page, but also any of the cog menus (Dashboard, Issue Navigator, and the sub-task list). The image below shows the Schedule option in the Issue Navigator:

Step 11. Creating an advanced dialog

Ideally, we would want avoid all page refreshes and refresh only the content that was updated. To do this, we'll override the default behavior of the form to write the new versions directly to the field.

  1. In Eclipse, edit scheduler.js.

  2. Remove the existing form code.

  3. Add the following form code:

    1
    2
    AJS.$(function () {
        // Function for getting the issue key of the issue being edited.
        var getIssueKey = function(){
            if (Jira.IssueNavigator.isNavigator()){
                return Jira.IssueNavigator.getSelectedIssueKey();
            } else {
                return AJS.$.trim(AJS.$("#key-val").text());
            }
        };
    
        // Function for getting the project key of the issue being edited.
        var getProjectKey = function(){
            var issueKey = getIssueKey();
            if (issueKey){
                return issueKey.match("[A-Z]*")[0];
            }
        };
    
        Jira.Dialogs.scheduleIssue = new Jira.FormDialog({
            id: "schedule-dialog",
            trigger: ".issueaction-schedule-issue", 
            ajaxOptions: Jira.Dialogs.getDefaultAjaxOptions,
            onSuccessfulSubmit : function(){  
    // This method defines behavior on a successful form submission.
    // We want to get the versions specified then place them in the view.
    // This selector gets the container for the FixFor Version for 
    // both a list of issues and the view issue page.
                var $fixForContainer = AJS.$("#issuerow" + Jira.IssueNavigator.getSelectedIssueId() + " td.fixVersions, #fixfor-val" );
                $fixForContainer.html("");  // Blank out the existing versions.
                // Now lets construct the html to place into the container.
                var htmlToInsert = "";
                // this.getContentArea() return the contents of the dialog.  From this we will get teh selected values of the select list and iterate over them.
                this.getContentArea().find("#fixVersions option:selected").each(function(){
                    var $option = AJS.$(this);
                    // We want to comma separate them.
                    if (htmlToInsert !== ""){
                        htmlToInsert += ", ";
                    }
                    var versionName = AJS.$.trim($option.text());
                    // Construct the link and append it to the html.
                    htmlToInsert += "<a href='" + contextPath + "/browse/" + getProjectKey() + "/fixforversion/" + $option.val()+ "' title='" + versionName + "'>" + versionName + "</a>";
                });
    
                // If no options were selected, insert none.
                if (htmlToInsert === ""){
                    htmlToInsert = AJS.I18n.getText("common.words.none");
                }
                // Set the html of the container.
                $fixForContainer.html(htmlToInsert);
            },
            onDialogFinished : function(){  
    // This function is used to define behavior after the form has finished.
    // We want to display a notification telling people that the fix version was updated.
    // When displayed in the Issue Navigator also show the issue key of the issue updated.
                if (Jira.IssueNavigator.isNavigator()){
                    Jira.Messages.showSuccessMsg(AJS.I18n.getText("scheduler.success.issue", getIssueKey()));
                } else {
                    Jira.Messages.showSuccessMsg(AJS.I18n.getText("scheduler.success"));
                }
            },
            autoClose : true // This tells the dialog to automatically close after a successful form submit.
    
        });
    });
    
  4. Save your work.

Step 12. Test the final version of the dialog

To test the app:

  1. Switch to a terminal window.

  2. If Jira is running, stop it.

  3. Change directory to the project root.
    This is the directory with the pom.xml file.

  4. Update the on-disk project metadata with the new POM information.

    1
    2
    atlas-mvn eclipse:eclipse
    
  5. Start Jira by running atlas-debug.

  6. Log in to Jira and test the new, advanced Schedule dialog.

Congratulations!

You should now have a fully working dialog.

Rate this page: