Building a simple search macro in Confluence
Asked by Bernd Steffens:

Hello,

i am new to plugin programming for Confluence. So far i worked through the related Atlassian tutorials and everything worked fine.

Now for reaching the next level I want to build a simple search macro. It should be able to search through all my Confluence pages and also works with the keyword AND (for two search requests) but excludes and. I know I could use the basic search function from Confluence, but my goal here is to learn plugin building.

My problem, as a beginner I am not really sure how to start. So far I created an empty plugin project and made the tweaks in the pom and the plugin descriptor. But what should I do next?


Answers:
author: DPKJ
body: [@Bernd Steffens](/t5/user/viewprofilepage/user-id/3670151) Welcome to the community!!!

I can give you brief idea about how to step further,

There are couple of scenario,

Static search result macro,

* In this macro you will get search term in macro input while creating macro
* and in result you will display results
* For this kind of macro,
  * Get started with custom macro module
  * Create one input for search text
  * On macro back-end code (java part) call confluence's search provider and fetch results in Java
  * Render these results in macro response

Dynamic search result macro,

* In this macro user will enter search term on confluence page and will see result in same page only
* For this,
  * Create a custom macro module
    * You won't need any input from user in start (later on you can add inputs in macro to limit search etc)
    * Your Macro code must return HTML with "INPUT" field, and space to show search result. (Basically it will need one form with input tag and button and empty div tag for results to appear)
  * Now you need to create a custom web resource module
    * In this you will create Javascript file (with global context)
    * This javascript, will trap submission of "form" returned by your macro
    * and later call some rest api (either standard or your custom api, rest plugin module is need to create custom api's)
    * and render results from this.

You can customize this scenario as much you like.

Happy Coding!!!

comments: author: Bernd Steffens
body: Thank you very much for your answer. So far I only have basic coding skills in Java. So the static solution seems more suitable for me. And actually my general idea of how to proceed is similiar:

* create custom macro module
* program an input mask in the macro
* implement the search function

But i'm kinda stuck on step 2. Read lots ot tutorials and documentation from Atlassian, but I don't really found what I was looking for. Can you help me taking the next step?

Thanks in advance!


author: DPKJ
body: You can proceed something like this,

In atlassian-plugin.xml file create a macro definition these lines,

```
<xhtml-macro key="search-macro"
             name="Search Macro"
             class="com.example.plugins.macro.SearchMacro"
             icon="/download/resources/icons/search.png" >
    <category name="`visuals`"/>
 <parameters>
 <parameter name="search" type="string" required="true"/>
 </parameters>
</xhtml-macro>
```

Now you need create a corresponding Java class to program this Macro,

```
package com.example.plugins.macro.SearchMacro

/* OTHER IMPORTS */

class SearchMacro implements Macro
{
  static final String TEMPLATE = "templates/macro/default.vm";

  public SearchMacro()
  {
  //You can bind search service here
  }

  @Override
  public BodyType getBodyType()
  {
    return BodyType.NONE;
  }

  @Override
  public Macro.OutputType getOutputType()
  {
    return OutputType.BLOCK;
  }

  @Override
  public String execute(Map<String, String> params, String body,
                        ConversionContext context) throws MacroExecutionException
  {
    String search = parmas.get('search'); //this your input search string
    return ""; // return template with search results
  }
```

I hope this resolves your basic problem of getting past step 2.

You can learn about searching in this example - <https://developer.atlassian.com/server/confluence/searching-using-the-v2-search-api/>


author: Bernd Steffens
body: Thanks for being so patient with me. I studied the article about using the v2 search API before, but it didn't work. But am I right that I need some kind of code like that for

```
 public SearchMacro()
  {
  //You can bind search service here
  }

```

and I am not sure what you mean here:

```
String search = parmas.get('search'); //this your input search string
```

does this create the input mask?

I'm sorry, but I'm getting more and more confused. I understand the basic concept, but can't cope to the details. :/


author: DPKJ
body: Take a look at this sample code I have managed to create for you.

<https://bitbucket.org/jangidd/sample-confluence-plugin/src/master/>


author: Bernd Steffens
body: Wow, thank you very much. After first checkup it looks quite similiar to code what I had. Only one difference, my version didn't work. :/ I have to take a closer look where my mistakes are.

Is it possible to modify the macro, that I put it on a Confluence page and have the search mask there? So that I can put my search string in the mask and not already in the macro configuration? Do you know what I mean?

It should be like the normal Confluence search field in the upper right corner, just again on a Confluence page. In my understanding this must be possible to achieve in the SearchMacro.java file? Or is there a better way? Or should that mask be created in the search-macro.vm?

Thanks again, you are very helpful!


author: DPKJ
body: [@Bernd Steffens](/t5/user/viewprofilepage/user-id/3670151)

Yes you can configure Macro to take input on user screen and not on Macro configuration screen. For this,

* You need to return simple html form (with input field) as macro response.
* After this, you need to add some Javascript and Rest API, to handle submission of form and display results.

You first give it a trial, if won't succeed, I will try and find some time to show you how this can be done.


author: Bernd Steffens
body: Ok thank you. I added this to the search-macro.vm:

\<form action="???" method="get"\>\>  
\<label for="searchmask"Search:\</label\>  
\<input type="text" id="searchword" name="searchword"\>  
\</form\>

It displays the searchmask on the Confluence space. But I'm not sure what to put in the action space, because I'm not having an URL in the usual way. I need to get my input from the form to the SearchMacro.java. Or is it better the other way around? I think this is what you described in the first point of your last answer. Am I right?

And can you point me to the direction in the java script where in the macro configuration screen is the search mask? I mean where is the input field in your version?


author: DPKJ
body: [@Bernd Steffens](/t5/user/viewprofilepage/user-id/3670151) I have found some time and update code for your need.

Please take look at same shared repository now, especially these files,

* <https://bitbucket.org/jangidd/sample-confluence-plugin/src/master/src/main/java/com/example/confluence/plugins/macro/DynamicSearchMacro.java>
* <https://bitbucket.org/jangidd/sample-confluence-plugin/src/master/src/main/resources/js/sample-plugin.js>

I have added a separate macro with key "dynamic-search-macro"

Hopefully, this will give you all that you need and point you to right direction.


author: Bernd Steffens
body: Thank you again, for devoting your time to my problem. Your solution definitly brings me closer to where I want to go.

For now I can't go on, because I need to understand your .js file first, that is way beyond my programming skills so far. I think when I get what you did there, I can go on with further features.


author: Bernd Steffens
body: So, after lots of reading and a couple of try outs I think I manged to understand the basic functionality of the .js file. Now I'm trying to go a step further. In my understanding the entered search term is stored in the variable "term" as a string.

I want to implement an "automatic" wildcard search option. That means the user can directly input his searchword and the macro is "adding" the chars "/." and "./" in front and at the end. For example: the user input is \*ouse, but the generated search request actually is /.\*ouse./

So I created a new string variable and used the String concat() method to add the needed characters. During output of the string I can see my desired result, so it works fine. But it seems like the getJSON() method can't handle it, because if I insert my variable the search doesn't work anymore. I tried different ways but, nothing succeded.

Is there another possibility to handle this? Or is my approach not the best way?


author: DPKJ
body: [@Bernd Steffens](/t5/user/viewprofilepage/user-id/3670151) Great going.

Have you tried encodeURI before appending search term to url in getJSON() method?

Take a look here - <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI>


author: Bernd Steffens
body: Thanks for this suggestion. I also assumed some problem with the special search characters and wasn't aware of the encodeURI() method. So now the encoding works fine, but it seems like the getJSON() method is still not accepting the modified search string. I don't really see where the problem is. Do you have another idea?


author: Bernd Steffens
body: After several hours of research and try outs it is now working the way I intended it. There is just one last task I'd like to accomplish. Right now the search results are displayed as text (the title of the site where the search word was found). Is it possible to make the results clickable (url) to go to the found confluence page directly?

I tried to get the url's in the $.getJSON() method, where the actual search is happening, but it didn't work. Is it possible to do it with the page_id every confluence has? I'm not really sure how to approach this. Any help is appreciated.


author: Bernd Steffens
body: Actually it would be nice to have the result presentation quite similiar to the standard confluence extended search function representation.


author: Deepanshu Natani
body: [@Monique vdB](/t5/user/viewprofilepage/user-id/786286):

[@DPKJ](/t5/user/viewprofilepage/user-id/690016)deserves the Random Acts of Community badge for this. He actually developed a Confluence Add-on and provided the entire code to help the community member.

On a different event, he helped a community member by developing an add-on for Jira!


Tags: code

confluence-server

new-to-confluence

search

server