How to show user message in ConfluenceActionSupport after execute()?

I’m new to Confluence plugin development, and also to web dev in general, so please bear with me.

I’ve written a plugin that adds an web-item to the page tools menu, by extending ConfluenceActionSupport. When the execute() method finishes, I would like to display a message to the user (an AUI Message would be nice). I tried calling ConfluenceActionSupport.addActionMessage() (and addActionError()), but they don’t seem to do anything. I’m running this code in atlas-debug. Am I doing something wrong?

Also, the action I’m performing doesn’t require any page refresh after it completes; I just need to display the user message. Is there an action result I can specify in the descriptor that doesn’t trigger an unnecessary refresh?

(Eventually, I also want to get input from the user in the form of a Yes/No dialog, as part of the server side logic, but I don’t want to get ahead of myself.)

As a web dev noobie, I confess that the division of labor between client and server isn’t entirely clear to me.

Any help would be greatly appreciated. TIA.

You’ll need to create your own view (velocity or soy) and then have your message appear based on that. This might be an older tutorial but might get you started: https://developer.atlassian.com/confdev/tutorials/adding-a-custom-action-to-confluence

Thanks very much for the feedback. The tutorial you cited is indeed the one I based my plugin on. I am currently redirecting to a velocity template based on the result string returned from execute().

But what I would prefer is to remain on the original page, and pop up a dialog box for the user input, and return a status message (a’la AUI Message) upon completion.

I assume there is some way to inject some JavaScript into all pages in which the web-item is rendered and make the web-item invoke the JS, which would contain the logic to conditionally pop up the user-input dialog (after using AJS to check with the server to determine whether the dialog is necessary), then show the completion message after the server-side logic completes.

But I don’t understand the plugin architecture well enough to figure out how to do this.

Ahh in that case you’re more looking at doing things using javascript and rest. You’ll still need your initial landing page (so you need the action) but then use web-resources to add your javascript on every page.
That javascript can then check for the existence of your web-item in the dom. If it’s there then connect up to it and when a user clicks on the web-item the javascript gets triggered and you’re in business.

https://bitbucket.org/dwester42a/p2-samples/src/2e154168b7cc986e08066cba1f7dbf1eea552d79/src/main/resources/atlassian-plugin.xml?at=4-web-resources&fileviewer=file-view-default has a sample web-resource in it. I believe that you could do something similar and then just use atl.general as the context and be attached to every page. At that point your javascript just has to use the jQuery module available at AJS.$

The flags are available at https://docs.atlassian.com/aui/5.7.48/docs/flag.html

Ok so I felt bad that I couldn’t find a tutorial for you that does what you want. So…

https://bitbucket.org/dwester42a/sample-confluence-showing-click-javascript

The basic of this is from the basic atlas-create-confluence-plugin-module command. So let’s get started. First we’ll add the rest module that we can trigger from javascript.

In the pom.xml I added in the rest dependency module: Bitbucket

<dependency>
            <groupId>com.atlassian.plugins.rest</groupId>
            <artifactId>atlassian-rest-common</artifactId>
            <version>2.9.7</version>
            <scope>provided</scope>
        </dependency>

This will let us get cracking on adding in the end point. This end point is going to be really simple and just return back the current date ( Bitbucket ). The end point is basically Jersey so there’s no Atlassian magic there.

@Path("/time")
public class CurrentDateEndPoint
{

This basically tells us the root of the paths is /time. Our method:

@Path("/fetch")
    @GET
    @Produces({MediaType.APPLICATION_JSON})
    public Response fetchCurrentTime()
    {
        Date d = new Date();
        Map<String, String> values = new HashMap<String, String>();
        values.put("date", d.toString());
        return Response.ok( values).build();
    }

We are responding to the GET method call at /time/fetch. And we’ll return back a application/json representation of the values hash map (this can be anything that Jackson/Jersey can serialize btw).

Then in the atlassian-plugin.xml we declare that we’ve got a rest end point ( Bitbucket ):

<rest key="some-rest-end-point" path="/my-rest-end" version="1.0">
        <description>General rest end point</description>
    </rest>

At this point we can call the rest end point at /rest/my-rest-end/1.0/time/fetch and get the current time. Let’s wire it up.

First let’s add the button. In the atlassian-plugin.xml ( Bitbucket ) we add:

<web-item key="my-butto" name="Some random button to add int" section="system.dashboard.button" weight="100">
        <label>Click me!</label>
        <link linkId="my-link-here">/</link>
    </web-item>

This will give us the button to appear on the dashboard. Now to wire it up with the rest call we’ll need some javascript. Now note the linkId attribute of the link node above. That usually becomes the id attribute of the link/button but in some rare cases there will be some “extra” prefix/suffixes added depending on the location. In this case we’re good though.

So we create our javascript file ( Bitbucket ).

AJS.toInit(function()
{

is an easy way to be called whenever the Atlassian product deems the page to be ready to be manipulated.

After that it’s mostly normal javascript. With the exception of:

AJS.contextPath()

This will always return back the context path of the web app.

The flag piece:

require(['aui/flag'], function(flag)
            {
                var myFlag = flag({
                   type: 'info',
                   title: 'Current time',
                   persistent: false,
                   body: 'The current time is '+ data.date
                });
            });

is basically from the page at https://docs.atlassian.com/aui/5.7.48/docs/flag.html

Now we just need to load the javascript on every page so back to atlassian-plugin.xml ( Bitbucket ):

<web-resource key="trigger-javascript-from-web-item-resources" name="trigger-javascript-from-web-item Web Resources">
        <dependency>com.atlassian.auiplugin:ajs</dependency>
        <dependency>com.atlassian.auiplugin:aui-flag</dependency> <!-- from https://docs.atlassian.com/aui/5.7.48/docs/flag.html -->
        
        <resource type="download" name="trigger-javascript-from-web-item.js" location="/js/trigger-javascript-from-web-item.js"/>

        <context>atl.general</context>
    </web-resource>

Where atl.general is pretty much every page (pretty much because every once in a while there are pages that don’t have it). The aui-flag dependency allows us to declare that we need access to the javascript that does the flag.

End result is that on the dashboard you get a button:

And when you click it:

2 Likes

@Daniel - Yes! Thank you so much. This is exactly what I was looking for. I couldn’t find anything like it, anywhere. It’s so generous of you to share your time and expertise. (Sorry for the delay in responding; I was on vacation.)

Also, your Web Fragment Finder add-on is super useful; I did not know it existed. I also did not know that Confluence source was available for commercial customers. I will download it next, but it will probably take a while to wrap my head around it. Finally, I found the recording of your talk online from Atlas Camp; also very helpful.

Time to roll up my metaphorical sleeves and do some coding… :slight_smile:

Hi @daniel, if you wanted to tie this button to a specific macro that is dropped on a page, can you give any advice? Thanks!