Board.id not working in sidebar

Hi,

This tutorial indicates that you can put ${board.id} in a web-item:
https://developer.atlassian.com/jiradev/jira-applications/jira-software/tutorial-adding-a-dropdown-to-an-agile-board.
I am trying to do this in the sidebar. For example:

<web-item key="tools" name="Tools" section="jira.project.sidebar.plugins.navigation" weight="1">
        <label key="boardbutton.label"/>
        <link linkId="tools_board">/plugins/servlet/my-page?boardId={$board.id}</link>
        <param name="iconClass" value="aui-icon aui-icon-large aui-iconfont-view-list"/>
        <context-provider class="com.atlassian.jira.plugin.webfragment.contextproviders.DefaultVelocityContextProvider" />
    </web-item>

I have tried multiple things and it does not seem to ever pass the board ID to my servlet. Is this a bug or do you need to do something tricky?

Cheers,
Justin

Hi @ubangy,

I think you’re missing the “$” on your board.id query parameter

See this:

<web-item section="my.custom-board-tool-section" key="my-custom-web-item" weight="10">
	<label key="gh.rapid.operations.configure"/>
	<link>/secure/RapidView.jspa?rapidView=${board.id}</link>
</web-item>

Hope that helps.

Cheers,
Anne

Anne, thanks for your response. I have tried this and it doesn’t work. Is it because I have a servlet within a page? The servlet does not seem to get the value. I am also assuming that boardId does not have to be rapidView!

Hey @ubangy,

What is the expected outcome here? Your web panel is attempting to add something to the project sidebar, which is a navigation component to get between every project-specific page, but you want to add something specific to a board. It’s a bit of a contradiction… the sidebar wouldn’t have the right contextual information to know what board you’d want to substitute in.

Technically, the “board” value would need to be in the context you provide to the <web-item> – in other words, the java class you reference in the <context-provider> block needs to specify that value. Right now, the context you’re providing doesn’t know anything about what board you’re wanting, so has no “board” value in its context, so cannot substitute a value for ${board.id}. Read https://developer.atlassian.com/docs/getting-started/plugin-modules/web-item-plugin-module#WebItemPluginModule-Context-providerElement for info on how to provide your own context.

Also, I’d check and confirm that your web-item works elsewhere first. As the tutorial you pointed to mentions, have you tried pushing your web panel to a <web-section> located in the “jira.agile.board.tools” section of the board pages? If you follow that tutorial, can you successfully add the button to the tools panel of the board you care about?

Cheers,
Chris.

@daz , thanks for your response.

The outcome is simply to have a link that goes to a page that I have defined:

<servlet key="my-page" class="com.mycompany.PageServlet">
        <url-pattern>/my-page</url-pattern>
</servlet>

So I guess I need to create my own context provider. I’m using spring boot, is there an example of autowiring rather than the way in the link you provided?

Thanks so much, you have given me some things to try.

Justin

I’m afraid that my knowledge of the back-end part of Jira / Atlassian-plugins is limited, so my advice here may be inaccurate.

I believe most P2 plugins can use the spring-scanner-plugin to provide dependency injection. Have a look at the https://bitbucket.org/atlassian/atlassian-spring-scanner README, and maybe this community question for a bit of extra detail if the README is insufficient on its own.

The question of wiring up your Java code aside, I’m concerned about how you’ll plan to select the “correct” board to put it in your context. Links in the sidebar will show up everywhere within in a project, including on pages that don’t have a board. And even when the user is on a page with a board, I’m not sure how your context provider would get access to that specific board.

Again, this could be my lack of experience with the server-side, so if you’ve got a plan, great! Otherwise, if it’s not supremely important that the link appear in the sidebar, I would stick it in the toolbar section on the boards page as in the tutorial (which should mean the board.id value is substituted correctly, since the context provided will have it), and you can iterate and solve this particular problem at a later date.

Hope that helps,
Chris

@daz,

I tried a few thinks and still can’t get it to work on cloud or server. One thing I have noticed is that if my web-item has page context it does not work. If I set context to add-on it works but then the page is not loaded correctly because it tries to load from my connect add-on URL and not the base URL. I hope this might help.

Cheers,
Justin

So I got this working locally by creating my own context provider and following https://developer.atlassian.com/jiradev/jira-platform/building-jira-add-ons/jira-plugins2-overview/jira-plugin-module-types/web-item-plugin-module.

It works when running under the SDK, however when I try to enable my plugin on a real Jira instance I get the following error:

org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [com.example.service.ContextParamsProviderImpl]; nested exception is java.io.FileNotFoundException: class path resource [com/atlassian/plugin/web/ContextProvider.class] cannot be opened because it does not exist
	at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:182)
	at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:321)
	at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanFactory(ConfigurationClassPostProcessor.java:261)
	at org.eclipse.gemini.blueprint.context.support.AbstractDelegatedExecutionApplicationContext.invokeBeanFactoryPostProcessors(AbstractDelegatedExecutionApplicationContext.java:444)
	at org.eclipse.gemini.blueprint.context.support.AbstractDelegatedExecutionApplicationContext.invokeBeanFactoryPostProcessors(AbstractDelegatedExecutionApplicationContext.java:414)
	at org.eclipse.gemini.blueprint.context.support.AbstractDelegatedExecutionApplicationContext.invokeBeanFactoryPostProcessors(AbstractDelegatedExecutionApplicationContext.java:362)
	at org.eclipse.gemini.blueprint.context.support.AbstractDelegatedExecutionApplicationContext$3.run(AbstractDelegatedExecutionApplicationContext.java:254)
	at org.eclipse.gemini.blueprint.util.internal.PrivilegedUtils.executeWithCustomTCCL(PrivilegedUtils.java:85)
	at org.eclipse.gemini.blueprint.context.support.AbstractDelegatedExecutionApplicationContext.startRefresh(AbstractDelegatedExecutionApplicationContext.java:220)
	at org.eclipse.gemini.blueprint.extender.internal.dependencies.startup.DependencyWaiterApplicationContextExecutor.stageOne(DependencyWaiterApplicationContextExecutor.java:224)
	at org.eclipse.gemini.blueprint.extender.internal.dependencies.startup.DependencyWaiterApplicationContextExecutor.refresh(DependencyWaiterApplicationContextExecutor.java:177)
	at org.eclipse.gemini.blueprint.context.support.AbstractDelegatedExecutionApplicationContext.refresh(AbstractDelegatedExecutionApplicationContext.java:157)
	at org.eclipse.gemini.blueprint.extender.internal.activator.LifecycleManager$1.run(LifecycleManager.java:207)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)
Caused by: java.io.FileNotFoundException: class path resource [com/atlassian/plugin/web/ContextProvider.class] cannot be opened because it does not exist
	at org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:172)
	at org.springframework.core.type.classreading.SimpleMetadataReader.<init>(SimpleMetadataReader.java:50)
	at org.springframework.core.type.classreading.SimpleMetadataReaderFactory.getMetadataReader(SimpleMetadataReaderFactory.java:98)
	at org.springframework.core.type.classreading.CachingMetadataReaderFactory.getMetadataReader(CachingMetadataReaderFactory.java:102)
	at org.springframework.core.type.classreading.SimpleMetadataReaderFactory.getMetadataReader(SimpleMetadataReaderFactory.java:93)
	at org.springframework.context.annotation.ConfigurationClassParser$SourceClass.getInterfaces(ConfigurationClassParser.java:797)
	at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:298)
	at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:232)
	at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:199)
	at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:168)
	... 15 more

Please help. Is there something I am missing in my atlassian-plugin.xml?

Cheers,
Justin

I have figured out that this is a Jira version issue. Works on Jira 7.2.2 not on Jira 7.5. What has changed? Can someone point me in the right direction?

Error when running under SDK is:

[INFO] [talledLocalContainer]     		Failed to parse configuration class [com.example.csacc.service.ContextParamsProviderImpl]; nested exception is java.io.FileNotFoundException: class path resource [com/atlassian/plugin/web/ContextProvider.class] cannot be opened because it does not exist
[INFO] [talledLocalContainer]     			class path resource [com/atlassian/plugin/web/ContextProvider.class] cannot be opened because it does not exist

Offending code is:

import com.atlassian.jira.plugin.webfragment.contextproviders.AbstractJiraContextProvider;
import com.atlassian.jira.plugin.webfragment.model.JiraHelper;
import com.atlassian.jira.user.ApplicationUser;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

@Component
public class ContextParamsProviderImpl extends AbstractJiraContextProvider {

    @Override
    public Map getContextMap(ApplicationUser user, JiraHelper jiraHelper)
    {
        Map<String, Object> contextParamsMap = new HashMap<>();
        if (jiraHelper != null && jiraHelper.getRequest() != null)
        {
            String id = jiraHelper.getRequest().getParameter("rapidView");
            if (id != null) {
                contextParamsMap.put("rapidView", id);
            }
        }
        return contextParamsMap;
    }
}

my pom.xml is:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>plugin</artifactId>
    <version>0.0.3</version>
    <organization>
        <name>Example</name>
        <url>http://www.example.com</url>
    </organization>
    <name>Some Plugin</name>
    <description>Some Plugin</description>
    <packaging>atlassian-plugin</packaging>
    <dependencies>
        <dependency>
            <groupId>com.atlassian.jira</groupId>
            <artifactId>jira-api</artifactId>
            <version>${jira.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.upm</groupId>
            <artifactId>licensing-api</artifactId>
            <version>${licensing.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.upm</groupId>
            <artifactId>upm-api</artifactId>
            <version>${licensing.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.atlassian.sal</groupId>
            <artifactId>sal-api</artifactId>
            <version>3.0.2</version>
            <scope>provided</scope>
        </dependency>
        <!-- Jersey -->
        <dependency>
            <groupId>javax.ws.rs</groupId>
            <artifactId>jsr311-api</artifactId>
            <version>${jersey.version}</version>
            <scope>provided</scope>
        </dependency>
        <!-- Utilities -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.0.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>1.1.0.Final</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <version>1.2</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
            <scope>provided</scope>
        </dependency>
        <!-- Velocity template renderer -->
        <dependency>
            <groupId>com.atlassian.templaterenderer</groupId>
            <artifactId>atlassian-template-renderer-api</artifactId>
            <version>${template.renderer.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity</artifactId>
            <version>1.7</version>
            <scope>provided</scope>
        </dependency>
        <!-- Spring dependencies -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
            <scope>provided</scope>
        </dependency>
        <!-- JSON support -->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>${gson.version}</version>
        </dependency>
        <!-- Unit testing -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-all</artifactId>
            <version>1.8.5</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>com.atlassian.maven.plugins</groupId>
                <artifactId>maven-jira-plugin</artifactId>
                <version>${amps.version}</version>
                <extensions>true</extensions>
                <configuration>
                    <productVersion>${jira.version}</productVersion>
                    <productDataVersion>${jira.version}</productDataVersion>
                    <enableQuickReload>true</enableQuickReload>
                    <enableFastdev>false</enableFastdev>
                    <compressResources>false</compressResources>
                    <pluginArtifacts>
                        <pluginArtifact>
                            <groupId>com.atlassian.labs.plugins</groupId>
                            <artifactId>quickreload</artifactId>
                            <version>${quick.reload.version}</version>
                        </pluginArtifact>
                    </pluginArtifacts>
                    <applications>
                        <application>
                            <applicationKey>jira-software</applicationKey>
                            <version>${jira.software.version}</version>
                        </application>
                    </applications>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <!-- inspiration from https://medium.com/@l1ambda/webpack-a-spring-boot-app-with-instant-page-refresh-5d051a09d26f -->
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <id>exec-npm-install</id>
                        <phase>initialize</phase>
                        <configuration>
                            <executable>npm</executable>
                            <arguments>
                                <argument>install</argument>
                            </arguments>
                            <workingDirectory>..</workingDirectory>
                        </configuration>
                        <goals>
                            <goal>exec</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>exec-npm-build</id>
                        <phase>generate-resources</phase>
                        <configuration>
                            <executable>npm</executable>
                            <arguments>
                                <argument>run</argument>
                                <argument>build-server</argument>
                            </arguments>
                            <workingDirectory>..</workingDirectory>
                        </configuration>
                        <goals>
                            <goal>exec</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>exec-npm-tests</id>
                        <phase>test</phase>
                        <configuration>
                            <executable>npm</executable>
                            <arguments>
                                <argument>run</argument>
                                <argument>test</argument>
                            </arguments>
                            <workingDirectory>..</workingDirectory>
                        </configuration>
                        <goals>
                            <goal>exec</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
                <includes>
                    <include>META-INF/spring/*.xml</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>false</filtering>
                <excludes>
                    <exclude>META-INF/spring/*.xml</exclude>
                </excludes>
            </resource>
        </resources>
    </build>
    <properties>
        <amps.version>6.3.6</amps.version>
        <jira.version>7.5.0</jira.version>
        <jira.software.version>7.5.0</jira.software.version>
        <quick.reload.version>1.30.2</quick.reload.version>
        <!-- JIRA provided dependencies -->
        <jersey.version>1.1.1</jersey.version>
        <licensing.version>2.0.1</licensing.version>
        <template.renderer.version>2.0.0</template.renderer.version>
        <spring.version>2.5.6.SEC01</spring.version>
        <!-- External dependencies -->
        <gson.version>2.3.1</gson.version>
        <atlassian.plugin.key>${project.groupId}.${project.artifactId}</atlassian.plugin.key>
        <author>Me</author>
    </properties>
</project>

Cheers,
Justin

Hello, Justin.

I have replied to you via this issue and asked for some items so we can help you figure this out.

Cheers,
Ian

For others had to add this to atlassian-plugin.xml:

<component key="contextProvider" class="com.example.ContextParamsProviderImpl" public="true">
    <interface>com.atlassian.plugin.web.ContextProvider</interface>
</component>

Hello!

I didn’t manage to get board.id by following both recommended approaches: from jira docs and from this topic.
Could someone please suggest whether there are working solutions and share them?

I’ve defined context params provider and registered it within web item element.
My link in atlassian-plugin.xml is:

/secure/MyAction.jspa?rapidViewId=${board.id}

Web item is placed at Greenhopper sprint drop down.

rapidViewId is always null. Tried to attach debugger to ContextParamsProviderImpl and added other params just to test:
sprintId is propagated correctly, while board.id is always absent.

Jira version is 7.13.0.

Are there any reliable way to fetch this parameter ? I can actually calculate it from URL in browser using JS trick, but I do not like this solution. I want to put to work some best practice solution.

Thanks!