Launch macro via button

Hello,

I have a java plugin that provides a macro which I would like to launch via a button on the page rather than automatically run every time the page is refreshed.

I’m able to return the html button from execute(). I’d then like the onclick to call another class method. This is what is not working for me. I’m afraid after execute() returns the macro may have gone out of scope. Is there a better way to do this? Here’s a snippet of what I tried:

    public String execute(Map<String, String> parameters, String body, ConversionContext conversionContext) throws MacroExecutionException {
    	this.conversionContext = conversionContext;

    	return "<input id=\"createinitiative\" type=\"button\" value=\"Create Initiative\" onclick=\"jiraRequest()\" >";
	}
	public String jiraRequest() {
        //run my code here
    }

Any advice would be much appreciated!

Thanks
Michael

After your execute method returns, that’s it on the server. The jiraRequest method will never be called. Once the client has rendered that (X)HTML, the click on the button will trigger the browser trying to find a global function called ‘jiraRequest’ - but obviously that will fail as there is no such function.
There are many ways to implement this, in a nutshell:

  • Use a web-resource to deliver that method (but scope the name of the method) to the browser and call the server via AJAX (if you need to).
  • Implement the server-side functionality of the jiraRequest method in a REST resource, don’t forget to check authentication & authorisation.

Read up on this and more on https://developer.atlassian.com/confdev/confluence-plugin-guide

You are returning HTML from your execute method, but this should be XHTML.

1 Like

Thanks for the reply. I was a afraid I went out of scope and you confirmed. Having the body of my code as a REST endpoint is a good idea. It also makes it more reusable by other modules. I’ll look to implementing that as a refactor.

I’m looking into the web-resource right now. I have the confluence-livesearch-plugin repo which I think is a good example project for how this works. I’ll post back here when I have a solution.

I’m having some trouble getting this started. Would there be any examples or snippets anyone could share? I’m looking to implement a button as well as a date and value picker.

Thanks!

I’ve thought on Christoffer’s suggestions a bit and I’m thinking to have execute() return a Velocity context. In that, I will load a .vm containing the (x)html for a button or picker. I will then have my jiraRequest() java method defined as a REST endpoint which can be the action of the button.

I’m currently having errors returning the Velocity context.

@ComponentImport
private final ApplicationLinkService applicationLinkService;
private final VelocityHelperService velocityHelperService;

@Autowired
public createInitiative(ApplicationLinkService applicationLinkService, VelocityHelperService velocityHelperService) { 
		this.applicationLinkService = applicationLinkService;
		this.velocityHelperService = velocityHelperService;
}

public String execute(Map<String, String> parameters, String body, ConversionContext conversionContext) throws MacroExecutionException {
	final Map<String, Object> contextMap = velocityHelperService.createDefaultVelocityContext();
	return velocityHelperService.getRenderedTemplate("templates/createInitiative.vm", contextMap);
}```

I’m putting some basic html in createInitiative.vm to test. This is unfortunately giving me runtime errors.

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘com.trustvesta.plugins.macro.createInitiative’: Unsatisfied dependency expressed through constructor argument with index 1 of type [com.atlassian.confluence.plugin.services.VelocityHelperService]: No qualifying bean of type [com.atlassian.confluence.plugin.services.VelocityHelperService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.atlassian.confluence.plugin.services.VelocityHelperService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency.

I’m using Spring with plugins-version=“2”.

Is there something I’m missing to inject a VelocityHelperService?

Hi @michael.r808 ,
try using VelocityUtils instead of VelocityHelperService.
Then return this method (static, so no need for injection):
VelocityUtils.getRenderedTemplate("templates/createInitiative.vm", contextMap);

For the default velocity context, use this static method:
Map<String, Object> contextMap = MacroUtils.defaultVelocityContext();

Best regards
Alex

1 Like

Hi @alexander.botor,

Thanks so much! I’m now able to return my .vm web-resource. Your suggestion was extremely helpful!

Michael

1 Like

I now have the button created in the .vm and the rest endpoint defined in the .java. The button displays when I insert the macro on a page. The rest endpoint is successfully invoked if I hit it via the browser url.

This must be simple but I can’t find a solid example. How can I connect the onclick of the html button to the rest endpoint? I assume some ajax wiring is need but nothing I’ve tried appears to be working.

Thanks,
Michael

A simple AUI button in a .vm template with an action to GET to the rest endpoint did the trick.

#requireResource("com.trustvesta.plugins:createinitiative-web-resources")

    <form id="createinitiative" class="aui" action="http://tgd-agile-4025:1990/confluence/rest/jirarequest/1.0/create/initiative" method="GET">
        <div class="field-group">
            <input type="submit" value="Create Initiative" class="button">
        </div>
    </form>
1 Like

Hi @michael.r808! Could I possibly see your final code? I’m trying to do the exact same thing and so far I’ve followed along your replies in this post and have looked at the confluence-livesearch-plugin, but not exactly sure what I’m doing wrong. I’m a little confused on whether you ended up using VelocityUtils, MacroUtils, or VelocityHelperService.