Creating JIRA plugin using custom UI framework

Hi all,

I’m working on a JIRA plugin and I would like to use 3rd party UI framework instead of the AUI. When I have added 3rd party framework’s resources, I noticed that there are some stylesheet clashes (same CSS classes defined in both AUI and 3rd party framework).

I could think of 2 approaches, but unfortunately neither of them worked:

  1. Ideally, I would like to isolate 2 frameworks, so that I could limit the use of AUI for page decoration (<meta name="decorator" content="atl.admin">) while using 3rd party framework to develop actual page contents.

    Unfortunately, I was unable to find any mechanisms provided by Plugin SDK to achieve such results. It seems that AUI is implemented in fairly “greedy” way where it attempts to be the one and only framework in town so even though I could scope 3rd party framework’s CSS, AUI CSS would still bleed onto my custom pages.

  2. My alternative approach was to remove AUI from my plugin pages completely (using <meta name="decorator" content="none">) and proceed with 3rd party framework only. But it appears that using <meta name="decorator" content="none"> disables loading of the webresources (or at least I was unable to make them work) so I am left with a blank html from my Velocity template and no means of loading packaged resources.

My questions are:

  1. Does anyone know how to limit the scope of AUI CSS?
  2. Does anyone know how to load packaged resources when page decorator is set to none?

Thanks in advance for any thoughts or comments.

P.S. I’m targeting JIRA Server v6.4 platform.

1 Like

Hi @ivan.ribakov,

you could try to wrap your HTML into an iframe. IIRC, this should prevent the CSS from the “outer” page from bleeding into the iframe. But this might also break web resources and it might also break AJS unless you’re able to inject them somehow (should be possible since the outer page and the iframe will come from the same domain).

Good luck!
Tobias

Hi @tobitheo,

Thanks for your answer. I thought of using the iframe but this approach still crashes against the problem described by me in the point 2, namely, even if I separate wrapper Page A from my plugin in Page B, Page B still has to be loaded without any of the default JIRA styles/scripts. But as soon as I remove all default styles/scripts from Page B, all other web resources stop loading on that page, making it impossible to load 3rd party framework resources.

The none decorator is the right approach. After that you’re on your own though (like you found) and have to inject your own resources. See “Referring to Web Resources” at https://developer.atlassian.com/server/framework/atlassian-sdk/web-resource-plugin-module/

@daniel Thanks for your answer Daniel. I have been trying to load necessary resources using web-resource but unfortunately I was unable to get any loaded with decorator set to none.

Can you spot if I’m missing anything from my setup below?

  • web resource definition:
  <web-resource key="my-resource" name="my-resource">
    <resource type="download" name="admin.css" location="/vuejs/css/admin.css"/>
    ...
  </web-resource>
  • Page controller class:
  public String doExecute() throws Exception {
    ...
    pageBuilderService.assembler().resources()
      .requireWebResource("com.acme.my-plugin:my-resource");
    return "success";
  }

Just to be clear - above configuration works fine with default decorators (atl.general & atl.admin) and I see my custom resource injected into the page, but as soon as I switch decorator inside Velocity template to none, I get blank html without standard JIRA or any of my custom resources loaded.

Do you by any chance have a working snippet that you could share?

You’re almost there.

In your code you’ll need to call
pageBuilderService.assembler().assembled().drainIncludeResources()
( see https://bitbucket.org/atlassian/atlassian-plugins-webresource/src/ef9ccea06afd81756ffee3c2917fcd774d5f1f09/atlassian-plugins-webresource-api/src/main/java/com/atlassian/webresource/api/assembler/AssembledResources.java?at=master&fileviewer=file-view-default )

That will return a WebResourceSet ( https://bitbucket.org/atlassian/atlassian-plugins-webresource/src/ef9ccea06afd81756ffee3c2917fcd774d5f1f09/atlassian-plugins-webresource-api/src/main/java/com/atlassian/webresource/api/assembler/WebResourceSet.java?at=master&fileviewer=file-view-default )

You can then use that to extract the html tags for you to put into your view.

1 Like

Hi @ivan.ribakov,

What 3rd party UI library are you using, and what parts of AUI are conflicting with it? Ideally we could update AUI to reduce or eliminate the conflicts you are experiencing. That won’t help solve your problem today, but it will help us remove it for everybody in the future.

Regarding your questions:

  1. Limiting the scope of AUI’s styles is not possible right now. Most AUI classes and attributes are prefixed with aui-to avoid conflict, but some older, deprecated components still use unprefixed values. AUI wants to remove all these unprefixed values to prevent conflicts, but in the short term you’re stuck with what it’s providing you via the Atlassian product.

  2. Atlassian products invoke the Web Resource Manager via decorators to load all UI assets for a given page. When you use no decorator, this does not happen. If you want to ensure your specific web-resources are loaded, Dan’s comments have you covered.

Cheers,
Daz.

@daniel Thanks for your pointers!
For anyone facing the same problem, here is the solution that worked for me in the end:

public String execute() throws Exception {

    WebResourceAssembler pageAssembler = pageBuilderService.assembler();

    // 0. Drain default JIRA resources
    pageAssembler.assembled().drainIncludedResources();

    // 1. Require custom resources necessary for your plugin
    pageAssembler.resources().requireWebResource("com.acme.my-plugin:my-resource");

    // 2. Drain custom resources required in step 1. and obtain corresponding WebResourceSet 
    WebResourceSet resources = pageAssembler.assembled().drainIncludedResources();

    // 3. Extract html tags (<link> & <script>) corresponding to required resources and store them in an object property
    Writer writer = new StringWriter();
    resources.writeHtmlTags(writer, UrlMode.AUTO);
    writer.flush();
    this.webResourceString = writer.toString();

    // 4. Do any other logic and return "success".
    return "success"
}

@HtmlSafe // Important to prevent html tags from being escaped when injected in Velocity template
public String getWebResources() {
    return webResourceString;
}

In your velocity template, place following markup to inject extracted resource tags:

<head>
    <title>...</title>
    <meta name="decorator" content="none"> <!-- Make sure decorator is set to none -->
    ${action.getWebResources()}
</head>

@daz I am using Vuetify. I’m not sure if the conflicting class is part of AUI (kinda lost in where one product ends and another starts in Atlassian), but I can confirm that class icon coming from css/batch.css was conflicting with icon class defined by Vuetify. Get your point about challenges in isolating styles so will stick with the solution described above for now.

3 Likes

Hey Ivan,
So where does this code go? I’m trying to figure out how to wrap by code in an Iframe to avoid my custom css from messing up the rest of the JIRA stylesheets.
Any chance I could see what you did?

Thanks
Casey