How do I store persistent values in a User Macro?

Neil
Contributor
August 27, 2014

Can I store persitent data in a user macro? (Or in the page of the user macro?)

So I have a small user macro that takes some params, makes some API calls, runs some jquery and spits out some numbers. I want to store part of these results in a persistent fashion on the page. Nothing fancy, just some numbers.

Is this doable in a user macro?

I am guessing there are some objects (like this one maybe?) that allow me to write some data into the content of the page... but I don't know enough about Java and Confluence to know where to start.

... Or if there is an easier way ;)

Here is some documentation I've already looked at. I assume the answer is in here, but I am not finding it.

Thanks for the help!!

1 answer

Comments for this post are closed

Community moderators have prevented the ability to post new answers.

Post a new question

5 votes
Stephen Deutsch
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
August 27, 2014

Hi Neil,

The easiest way I've found was to inject the ContentPropertyManager into the page. You can do that with the following code:

#set ( $containerManagerClass=$content.class.forName('com.atlassian.spring.container.ContainerManager') )
#set ( $getInstanceMethod=$containerManagerClass.getDeclaredMethod('getInstance',null) )
#set ( $containerManager=$getInstanceMethod.invoke(null,null) )
#set ( $containerContext=$containerManager.containerContext )
#set ( $contentPropertyManager=$containerContext.getComponent('contentPropertyManager') )

This will allow you to use the methods in the contentPropertyManager class (you can see them here). This allows you to store a string and it is persisted in the page. Here is an example of how to do it:

$contentPropertyManager.setTextProperty($content, "metadata.key", "value to store")

Be aware that if you use the setStringProperty method, you are limited to 256 characters. Since you want to store multiple values, I usually save them with commas inbetween, and then you can split it (i.e. $string = $string.split(",") ).

Otherwise, if you have the latest version of Confluence (5.5) and you want to get really fancy, you could use the new content properties API, with which you can store up to 32KB of JSON at a time. For more information, look at this presentation slides 23-25.

You would just make an ajax call using jQuery, for example:

jQuery.ajax({
  contentType: 'application/json',
  type: 'POST',
  url: contextPath + "/rest/api/content/" + AJS.params.contentId + "/property",
  data: myJsonData,
  success: function(response) {
    console.log(response);
  }
});

Note: when writing user macros, I found that using "jQuery" instead of "AJS.$" works better, because velocity sometimes interprets the $ as a variable, and it can sometimes fail silently.

If you want to go this route, I would suggest installing the REST API browser on your instance. Then you can see which query parameters you can include, and run tests right in your browser. The benefit of this method is that you don't even have to worry about writing velocity code at all.

Hope that helps!

Davin Studer
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
August 29, 2014

Another way to get around velocity not liking AJS.$ and sometimes the pound in a jquery id selector is this...

## @noparams

#set ($d = "$")
#set ($p = "#")

<script type="text/javascript">
AJS.toInit(function(){
    AJS.${d}('${p}myElementId').foo();
});
</script>

Stephen Deutsch
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
August 31, 2014

Thanks for the tip Davin! I'll have to try it out...

Neil
Contributor
September 3, 2014

Wow, thanks for the awesome response! I didn't get (or missed) a notification that there were any responses, so I just noticed your comments yesterday.

I was able to use contentPropertyManager to set and get properties but only hard coded strings. I wasn't able to reference variables from my Javascript because (I think) all those java calls happen server side? (is velocity responsible for that?)

Is there a way to call contentPropertyManager methods *after* the page loads?

The second option you list is ideal. If I could just stick JSON in a key value pair in the properties of the page I would be ecstatic.

When I use AJS.params.contentId in my Javascript I get "undefined". If I put that directly in the Javascript console I get the right number, but not from in the page, even when it is in an onload event... I am know I am doing something wrong, but I just manually plugged the Id in for now.

When I try to post to /rest/api/id/property, like in your example, I get a 404. Which is wierd because the doc says that is not an option. I am running 5.5.3

When I get /rest/api/id/property I also get a 404. Not sure if that is expected or not. There are several key value pairs I have stored in the page using contentPropertyManager methods. Are both mechanisms addressing the same objects? If I have a key:value of "hello":"world" set via contentPropertyManager, should I expect to see that value via /rest/api/id/property/hello? I get a 404 for that endpoint. I am authenticated to Confluence in other tabs in Chrome, so I assume that is valid. I am clearly missing something...

* Is there a way to call contentPropertyManager methods *after* the page loads?

* Is there something else I need to do in order to be able to use the /propperty/ endpoint?

Thanks for all your help.

EDIT: So I installed the REST API Browser and sure enough there is no property endpoint listed. Am I some how not on the correct version? I am on

Confluence Version 5.5.3
Build Number 5515
Stephen Deutsch
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
September 3, 2014

Hi Neil,

As far as I know the contentPropertyManager storage is completely separate from the contentProperty REST call, so I don't think you'll be able to access it using a REST call later.

Were you able to install the REST Browser on your instance? That's the quickest way of knowing whether that command is available or not. After a quick check, it seems like the content property path was first officially released in 5.6 that came out a few days ago, but I'm not sure if it was available in other releases unofficially.

The way that I've managed to save contentProperties after the page loads is to reload the page with a request sent over the URL (i.e. /display/SPACE/Page+Name?myThingToChange=newThing). You can get the request sent with the page load by using $req.getParameter("myThingToChange").

If you're mixing velocity and javascript, then you could probably just use $content.id to get the page ID.

If you're making a rest call, you should make sure that you do it in the same tab in which confluence is loaded, otherwise even if Confluence is authenticated in another tab, it probably won't work.

If you want to see some examples of how I used contentProperties in a user macro, you should check out my repository on bitbucket:

https://bitbucket.org/stephendeutsch/confluence-user-macros/src

If there's anything else I can do to help you out, let me know.

TAGS
AUG Leaders

Atlassian Community Events