Hello,
I’m trying to port a Cloud plugin I made, and to do that I need to display a dialog in response to both a web item and a normal link.
What I’m trying to achieve
I need to display a custom dialog with custom forms inside it. The “Submit” button should store custom JS-defined data in an issue. (see below for a screenshot)
So a basic use case would be:
- The user clicks on a normal link (which is in an issue webpanel) or a webitem (which is in
opsbar-operations
) - The dialog opens
- The user can filter on a specific DataGalaxy project/version
- The user has to search for a DataGalaxy object and select one (pressing Enter should search, not close the dialog)
- The user clicks Submit, and the new object is linked to the issue
I’m currently working on displaying the dialog properly, data storage will come later.
First try with webwork
I tried to use a webwork to achieve my goal:
<webwork1 key="dg-issue-button-action" name="Link DG Objects" class="java.lang.Object">
<actions>
<action name="com.datagalaxy.app.webwork.LinkObjects" alias="LinkObjects">
<view name="input">/templates/issue_dialog.vm</view>
</action>
</actions>
</webwork1>
<web-section key="dg-issue-button-section" location="opsbar-operations" weight="900">
<label key="datagalaxy.issue.section.label" />
</web-section>
<web-item key="dg-issue-button" section="dg-issue-button-section" weight="10">
<label key="datagalaxy.issue.button.label" />
<tooltip key="datagalaxy.issue.button.tooltip" />
<link linkId="dg-issue-dialog-link">/secure/LinkObjects!default.jspa?id=${issue.id}</link>
<styleClass>trigger-dialog</styleClass>
</web-item>
This is my JiraWebActionSupport subclass:
package com.datagalaxy.app.webwork;
import com.atlassian.jira.web.action.JiraWebActionSupport;
@SuppressWarnings("serial")
public class LinkObjects extends JiraWebActionSupport
{
public LinkObjects() {
}
@Override
public String doDefault() throws Exception {
return INPUT;
}
// @Override
// protected void doValidation() {
// }
@Override
public String doExecute() throws Exception {
// return returnCompleteWithInlineRedirect("/browse/" + getIssueObject().getKey());
return INPUT; // INPUT doesn't close the dialog, but if i use SUCCESS instead, the "Submit" button will cause a failure
}
}
And this is the JS code needed to display the dialog properly (but only for the webitem, the normal link works properly without it, even if both uses “trigger-dialog” CSS class, maybe related to this issue):
(function ($) {
new AJS.FormPopup({
id: "dg-issue-dialog",
trigger: "#dg-issue-dialog-link"
});
})(AJS.$ || jQuery);
But even if my dialog is rendering properly now, I have many issues with the “Submit” button.
My dialog uses two different forms, let’s call them “Subform 1” and “Subform 2”, one is used to filter on projects and versions, the other is used to search objects over DataGalaxy API.
But the dialog itself is a form, and nested forms won’t work, so how am I supposed to handle this?
My dialog renders like so:
The first encountered button will be the submit button for all the form, so here the “Search…” button will act as the “Submit” button of the dialog form (“Form 1”), which isn’t the behaviour I expected.
Is there a way to display a non-form Dialog with a webwork?
I tried to move “Form 1” to the bottom of the template, so that Subforms 1 and 2 aren’t nested anymore. That fixes the “Submit” button style, but the submit button fails to find my action. This is the form:
<form class="aui" action="LinkObjects.jspa" method="post">
<input type="hidden" name="atl_token" value="${atl_token}" />
<input type="hidden" name="id" value="$action.id" />
<div class="buttons-container content-footer">
<div class="buttons">
<a href="#" class="cancel">$i18n.getText('datagalaxy.issue.dialog.cancel')</a>
<input class="button" type="submit" value="$i18n.getText('datagalaxy.issue.dialog.submit')" />
</div>
</div>
</form>
When all my content is inside the form, it manages to recognize “LinkObjects.jspa” as “${baseurl}/secure/LinkObjects.jspa”, but when I separate this form and put it at the bottom of my template, the submit button will try to send me to “${baseurl}/browse/LinkObjects.jspa”. It’s weird, but ok.
If replace action="LinkObject.jspa"
by action="${baseurl}/secure/LinkObjects.jspa"
and click on “Submit”, I get a 404…
And there’s better: the “Search…” button will now act as a “Submit” button, but it won’t do any action beside blocking both buttons. And it won’t run the JS function contained in its “onclick” attribute.
Second try with servlets + AUI Dialog2
Code
The relevant part in atlassian-plugin.xml:
<servlet key="dg-issue-dialog-servlet" class="com.datagalaxy.app.servlet.DialogServlet">
<url-pattern>/datagalaxy/dialog</url-pattern>
</servlet>
And this is my servlet:
package com.datagalaxy.app.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URI;
import com.atlassian.sal.api.auth.LoginUriProvider;
import com.atlassian.sal.api.user.UserManager;
import com.atlassian.templaterenderer.TemplateRenderer;
import javax.inject.Inject;
import com.atlassian.plugin.spring.scanner.annotation.component.Scanned;
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
@Scanned
@SuppressWarnings("serial")
public class DialogServlet extends HttpServlet
{
@ComponentImport
private final UserManager userManager;
@ComponentImport
private final LoginUriProvider loginUriProvider;
@ComponentImport
private final TemplateRenderer renderer;
@Inject
public DialogServlet(UserManager userManager, LoginUriProvider loginUriProvider, TemplateRenderer renderer)
{
this.userManager = userManager;
this.loginUriProvider = loginUriProvider;
this.renderer = renderer;
}
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
String username = userManager.getRemoteUsername(request);
if (username == null) {
redirectToLogin(request, response);
return;
}
response.setContentType("text/html;charset=utf-8");
renderer.render("templates/issue_dialog.vm", response.getWriter());
}
private void redirectToLogin(HttpServletRequest request, HttpServletResponse response) throws IOException
{
response.sendRedirect(loginUriProvider.getLoginUri(getUri(request)).toASCIIString());
}
private URI getUri(HttpServletRequest request)
{
StringBuffer builder = request.getRequestURL();
if (request.getQueryString() != null) {
builder.append("?");
builder.append(request.getQueryString());
}
return URI.create(builder.toString());
}
}
Actual behaviour
I tried to use the example for AUI Dialog 2 from here: Dialog2 - AUI Documentation
What I’m expecting:
What I get with the same code (notice the close link is outside the dialog):
What I get when I remove the search bar + the close button and add my content in the dialog:
Why is the rendering cropped like this? Why is there two distinct headers in the dialog?
Conclusion
I really didn’t think it would be so hard to display a custom dialog in Jira Server, compared to Jira Cloud. Why things need to be so complex for such a basic task?
Thanks in advance,
Quentin Bazin