Create
cancel
Showing results for 
Search instead for 
Did you mean: 
Sign up Log in

Custom dialog does not open with <script> tag

Fabian Laqua May 26, 2021

Hello,

i have a  problem with my custom dialog in jira.

I created a custom dialog with the rest endpoint and fragment item and this works very well. The dialog opens and shows alle fields, tables. But now i need some javascript to give this dialog some functions. But i dont know, how to do it. I followed this blog from adaptavist.

If i add the <script>...</script> tag, the dialog wont open. And i cant find an error message, what the reason could be. 

What i`m doing wrong? I hope you could help me.

I found the Dialogs (Advanced) documentation from adaptavist, but i dont understand js part below. What should i do? I hope you guys can guide me to the solution to successfully using javascript.

This is my rest endpoint script:

import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.issue.Issue
import groovy.transform.BaseScript
import javax.ws.rs.core.MediaType
import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response

@BaseScript CustomEndpointDelegate delegate

static getUsersFromAD(String adGroup) {
def groupManager = ComponentAccessor.getGroupManager()
Collection users = groupManager.getUsersInGroup(adGroup)

return users[0].getDisplayName()
}


static getDialogContent(String issueKey) {
return """
<script type="text/javascript">

</script>

<section
role="dialog"
id="sr-dialog"
class="aui-layer aui-dialog2 aui-dialog2-medium"
aria-hidden="true"
data-aui-remove-on-hide="true"
>
<header class="aui-dialog2-header">
<h2 class="aui-dialog2-header-main">Edit cost units</h2>
<button class="aui-close-button" type="button" aria-label="close"></button>
</header>

<div class="aui-dialog2-content">
<div id="Container" style="width:px">
<form class="aui">

<div id="colmn1" class="field-group">
<label for="ktr-input">KTR</label>
<input id="ktr-input" class="text" type="number" name="ktr-input"/>
</div>

<div id="colmn2" class="field-group">
<label>KTR Identifkation</label>
<div>
<input type="radio" id="flag-3" name="flag" value="3" checked>
<label for="flag-3">first 3</label><br>
</div>
<div>
<input type="radio" id="flag-complete" name="flag" value="complete">
<label for="flag-complete">Complete</label><br>
</div>
</div>

<div id="colmn3" class="field-group">
<label for="select-responsible">responsible</label>
<select class="select" id="select-responsible" name="select-responsible">
<option>Person X</option>
<option>Person Y</option>
<option>Person Z</option>
</select>
</div>

<div id="colmn4" class="field-group">
<label for="select-gf">Manager</label>
<select class="select" id="select-gf" name="select-gf">
<option>Person X</option>
<option>Person Y</option>
<option>Person Z</option>
</select>
</div>

<div style="float: right; margin-top: 10px; margin-bottom: 20px;">
<button id="addToTable" class="aui-button aui-button-primary">add</button>
<button id="removeFromTable" class="aui-button">clear</button>
</div>

<table class="aui">
<thead>
<tr>
<th id="KTR">KTR</th>
<th id="Responsible">Responsible</th>
<th id="GF">GF</th>
<th id="Action">Action</th>
</tr>
</thead>
<tbody>
<tr>
<td headers="KTR">733</td>
<td headers="Responsible">Person X</td>
<td headers="GF">Person Y</td>
<td headers="Action">
<button class="aui-button aui-button-link" id="edit1">Edit</button>
<button class="aui-button aui-button-link" id="delete1">Delete</button>
</td>
</tr>
<tr>
<td headers="KTR">378</td>
<td headers="Responsible">Person Z</td>
<td headers="GF">Person ZZ</td>
<td headers="Action">
<button class="aui-button aui-button-link" id="edit2">Edit</button>
<button class="aui-button aui-button-link" id="delete2">Delete</button>
</td>
</tr>
</tbody>
</table>

</form>
</div>
</div>

<footer class="aui-dialog2-footer">
<div class="aui-dialog2-footer-actions">
<button class="aui-button aui-button-primary" id="submit">Update</button>
<button id="dialog-close-button" class="aui-button aui-button-link"">Close</button>
</div>
<div class="aui-dialog2-footer-hint">This is a footer message</div>
</footer>
</section>
"""
}

// REST
showEditDialogCostUnit(groups: ["jira-administrators"]) { MultivaluedMap queryParams ->
def issueKey = queryParams.getFirst("issueId") as String
//String users = getUsersFromAD("")
String dialog = getDialogContent(users);
Response.ok().type(MediaType.TEXT_HTML).entity(dialog.toString()).build()
}


My goal is to give these buttons some functionalities. 

Our jira test system: v8.13.6, Scriptrunner: 6.23.0

Thx for your help.

1 answer

1 accepted

Suggest an answer

Log in or Sign up to answer
1 vote
Answer accepted
Peter-Dave Sheehan
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
May 26, 2021

Try putting the script tag just before the close of the <section>.

You might want to explore using the MarkupBuilder to create your form. In my opinion, it is easier to manage than a huge long string. Especially if you have to repeat information such as for your table.

Here is your script converted up to the first field, with a little extra to get and include the javascript stored in a  different file.

I've also added your dummy data as a map in the getUserFromAD to demonstrate how you can get that data and iterate through it.

import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import com.atlassian.jira.component.ComponentAccessor;
import com.atlassian.jira.issue.Issue
import groovy.transform.BaseScript
import groovy.xml.MarkupBuilder
import com.atlassian.jira.config.util.JiraHome

import javax.ws.rs.core.MediaType
import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response

@BaseScript CustomEndpointDelegate delegate

static getUsersFromAD(String adGroup) {
//def groupManager = ComponentAccessor.getGroupManager()
//Collection users = groupManager.getUsersInGroup(adGroup)

//return users[0].getDisplayName()
def usersMap = [
[ktr:733, responsible: 'person x', gf: 'person y'],
[ktr:378, responsible: 'person z', gf: 'person zz'],
]
}


static String getDialogContent(String issueKey) {
def jiraHome = ComponentAccessor.getComponent(JiraHome).home
def xmlWriter = new StringWriter()
def xml = new MarkupBuilder(xmlWriter)
xml.secion(role: 'dialog', id: 'sr-dialog', class: 'aui-layer aui-dialog2 aui-dialog2-medium', 'data-aui-remove-on-hide': 'true') {
header(class: 'aui-dialog2-header') {
h2(class: 'aui0dialog-2-header-main') { xml.mkp.yield('Edit cost units') }
button(class: 'aui-clone-button', type: 'button arial-label', ' aria-label':'close')
}
div(class: "aui-dialog2-content") {
div(id: "Container", style: "width:px") {
form(class: "aui") {
div(id: "colmn1", class: "field-group") {
label (for: "ktr-input") { xml.mkp.yield 'KTR' }
input(id: "ktr-input", class: "text", type: "number", name: "ktr-input")
}
}
}
}
table(class:'aui') {
thead {
tr {
th(id: 'KTR') { xml.mkp.yield('KTR') }
th(id: 'Responsible') { xml.mkp.yield('Responsible') }
th(id: 'GF') { xml.mkp.yield('GF') }
th(id:'Action'){ xml.mkp.yield('Action')}
}
}
tbody{
getUsersFromAD().eachWithIndex{user, index->
tr{
td(headers:'KTR'){xml.mkp.yield user['ktr']}
td(headers:'KTR'){xml.mkp.yield user['responsible']}
td(headers:'GF'){xml.mkp.yield user['gf']}
td(headers:'KTR'){
button(class:"aui-button aui-button-link",id:"edit$index"){xml.mkp.yield('Edit')}
button(class:"aui-button aui-button-link",id:"delete$index"){xml.mkp.yield('Delete')}
}
}
}
}
}
script(type:'text/javascript'){
def scriptFile = new File("$jiraHome/path/to/you/javascript/file.js")
xml.mkp.yieldUnescaped( scriptFile.getText('UTF-8'))
}
}
return xmlWriter.toString()
}

// REST
showEditDialogCostUnit(groups: ["jira-administrators"]) { MultivaluedMap queryParams ->
def issueKey = queryParams.getFirst("issueId") as String
//String users = getUsersFromAD("")
String dialog = getDialogContent(issueKey);
Response.ok().type(MediaType.TEXT_HTML).entity(dialog).build()
}
Fabian Laqua May 31, 2021

@Peter-Dave Sheehan 

Thx a lot. This helped me a lot. The MarkupBuilder is great. I wasn`t aware of this. But now im capable to debug a little bit better :-)

i struggled a little bit with your script file explaination: "$jiraHome/path.." and tried several times and now this seems to work for me and i understand your "$jiraHome" :-)
def scriptFile = new File("E:/Atlassian/.../myscript.js")
After that i added to my button an "onclick="myTest()" with an console.log()" and this seems to work either. There is a log in the console.

Do you have any hints or tips to do it in a better way? Maybe there are some build in functions i can use?!

And how can i populate my changes back to the issue? Is it possible with js? At this moment, i have no clue how i can do this.

Best regards

Peter-Dave Sheehan
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
June 1, 2021

Are you familiar with the script root concept of scriptrunner?

In your jira-home directory, there should be a folder called "scripts". There were all the files to create and manage with the Script Editor will go.

My suggestion is to store your .js file in there. And rather than hardcoding the full path, for better portability, you get the jiraHome directory and access the file using a relative path from there.

As for how to populate your changes back to the issue, you will need to use ajax or equivalent methods from javascript to call built-in or customer REST end-point in jira.

Fabian Laqua June 6, 2021

Hello @Peter-Dave Sheehan 
i tried again the jira-home-directory and it seems to work. I guess i made some misstakes earlier. My .js file is located under the script folder. Thanks for your advice.
About the probleme to populate the changes back:
I would not want to build another REST endpoint. I think without a second endpoint it is more clearly arranged.
Can you show my an example how to use ajax or an equivalent method like you mentioned before? Something like changing a field "customfield_12034". Thanks a lot for your help and advices.

Peter-Dave Sheehan
Community Leader
Community Leader
Community Leaders are connectors, ambassadors, and mentors. On the online community, they serve as thought leaders, product experts, and moderators.
June 7, 2021

Here is an example

var customField = AJS.$('#html-id-of-the-field-in-your-dialog');
var issueKey = ''; //you'll need some way to access the issue. Maybe add a hidden field in your dialog
AJS.$.ajax({
type: "PU",
dataType: "json",
"contentType": "application/json",
headers: { "X-Atlassian-Token": "no-check" },
data: {
"fields":{
"customfield_xxxxx": customField.val()
}
},
url: `/rest/api/latest/issues/${issueKey}`,

beforeSend: function (){
//something you want to happen before making the call
},

success: function (data, status){
//something you want to do after the call returns success
},

error: function (data){
//something to do in case you have an error
console.error(data);
},
complete: function(){
//something you want to happen at the end of ajax call regardless of status
},
});
Fabian Laqua June 8, 2021

Thx, it helped me a lot.
But i had a problem with the data. When i tried execute put request i received a 400. This was the error message:

errorMessages\":[\"Unrecognized token 'fields': was expecting 'null', 'true', 'false' or NaN...

A JSON.stringify(...) fixed it for me.

 

...
var payload = {
"fields":{
"customfield_10443" : mydata
}
};

payload = JSON.stringify(payload);

AJS.$.ajax({
type: "PUT",
dataType: "json",
contentType: "application/json",
headers: { "X-Atlassian-Token": "no-check" },
data: payload,
url: `/rest/api/latest/issue/${issueKey}`,
...

 

TAGS
AUG Leaders

Atlassian Community Events