I have a Web Item added by scripted fragment to open up a dialog. How can I add a Rich Text Editor for comment input to the dialog? For example, the editor you see after you click the Assign user button.
Thanks in advance for any tip.
Hi Zhiyong,
Our team just finalized a working version of Rich Text in a custom dialog, so hopefully by sharing it here we can help you, or any others seeking this functionality.
There may still be attributes that can be removed from the markup, but, so far, this is as clean as I've managed while retaining good function. Here is the REST Endpoint code+markup:
import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import groovy.transform.BaseScript
import javax.ws.rs.core.MediaType
import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.config.properties.APKeys
@BaseScript CustomEndpointDelegate delegate
richTextCommentDialog() { MultivaluedMap queryParams ->
String issueId = queryParams.getFirst("issueId")
String issueKey = queryParams.getFirst("issueKey")
String baseUrl = ComponentAccessor.getApplicationProperties().getString(APKeys.JIRA_BASEURL)
def dialog =
"""
<div id="rt-dialog" class="jira-dialog box-shadow popup-width-custom" data-aui-modal="true" data-aui-remove-on-hide="true" style="width: 810px; margin-left: -406px; margin-top: -434.5px;">
<div class="jira-dialog-heading">
<h2 title="Rich-text comment on: $issueKey">Rich-text comment on: $issueKey</h2>
</div>
<div class="field-group aui-field-wikiedit comment-input">
<div class="jira-wikifield" field-id="comment" renderer-type="atlassian-wiki-renderer" issue-key="$issueKey" resolved="">
<div class="wiki-edit">
<div id="comment-wiki-edit" class="wiki-edit-content">
<textarea class="textarea long-field wiki-textfield mentionable wiki-editor-initialised" cols="60" id="commentText" name="commentText" wrap="virtual" data-issuekey="$issueKey" resolved="" style="min-height: 174px; max-height: 540px;"></textarea>
<div class="rte-container">
<rich-editor contenteditable="true"></rich-editor>
</div>
</div>
</div>
<div class="field-tools">
<button class="jira-icon-button fullscreen wiki-preview" id="comment-preview_link" type="button">
</button>
</div>
</div>
<div class="save-options wiki-button-bar">
</div>
<div class="buttons-container form-footer">
<div class="buttons"><span class="icon throbber"></span><button id="rt-dialog-submit" class="aui-button">Submit</button><button id="rt-dialog-cancel" class="aui-button aui-button-link">Cancel</button>
</div>
</div>
</div>
<script>
AJS.dialog2("#rt-dialog").on("show", function() {
document.getElementById('commentText').focus();
});
AJS.\$("#rt-dialog-submit").click(function(e) {
var text = AJS.\$("#commentText").val();
var comment = {"body": text
};
AJS.\$.ajaxSetup({
baseUrl: '""" + baseUrl + """'
});
AJS.\$("#rt-dialog-submit").before('<span class="aui-icon aui-icon-wait">Submitting...</span>');
AJS.\$("#rt-dialog-submit").prop("disabled", true);
AJS.\$.ajax({
url : '/rest/api/2/issue/""" + issueId + """/comment',
headers: { "Content-Type" : "application/json" },
type: 'POST',
data : JSON.stringify(comment),
cache : false,
success: function(response){
JIRA.trigger(JIRA.Events.REFRESH_ISSUE_PAGE, [JIRA.Issue.getIssueId()]);
JIRA.trigger("GH.RapidBoard.causeBoardReload");
var myFlag = AJS.flag({
type: 'success',
title: 'Comment added successfully',
close: 'auto'
});
AJS.dialog2("#rt-dialog").remove();
},
error: function(response){
console.log(JSON.stringify(response));
var myFlag = AJS.flag({
type: 'error',
title: 'Oh no!',
body: JSON.parse(response.responseText).errors.comment,
close: 'manual'
});
AJS.dialog2("#rt-dialog").remove();
}
});
});
AJS.\$("#rt-dialog-cancel").click(function(e) {
AJS.dialog2("#rt-dialog").remove();
});
</script>
</div>
"""
Response.ok().type(MediaType.TEXT_HTML).entity(dialog.toString()).build()
}
Things to note:
We didn't pursue this just for kicks though. We have a client group that will be using this to easily add comments that have visibility restricted to a specific project role. Please see my follow-up post for the Script Fragment and REST endpoint that include Developer restricted button and comment visibility.
Hope this helps!
Josh
Thanks very much Josh for sharing the solution. I will integrate it with my code.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You're most welcome Zhiyong. Happy to help!
As mentioned, I have an expanded example that I'll post here as well (comment with 'comment visible to Developers', and visibility of the button restricted to that role).
Here is the Script Fragment:
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.project.ProjectManager
import com.atlassian.jira.security.roles.ProjectRoleManager
import com.atlassian.jira.security.roles.ProjectRoleActors
if (jiraHelper.project?.key != "<Your project's KEY here>")
return false
def currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
ProjectManager projectManager = ComponentAccessor.getProjectManager()
ProjectRoleManager projectRoleManager = ComponentAccessor.getComponent(ProjectRoleManager)
ProjectRoleActors developers = projectRoleManager.getProjectRoleActors(projectRoleManager.getProjectRole("Developers"), issue.getProjectObject())
return developers.contains(currentUser)
And here is the REST Endpoint:
import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import groovy.transform.BaseScript
import javax.ws.rs.core.MediaType
import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.config.properties.APKeys
@BaseScript CustomEndpointDelegate delegate
addDeveloperPermissionedCommentDialog() { MultivaluedMap queryParams ->
String issueId = queryParams.getFirst("issueId")
String issueKey = queryParams.getFirst("issueKey")
String baseUrl = ComponentAccessor.getApplicationProperties().getString(APKeys.JIRA_BASEURL)
def dialog =
"""
<div id="dev-only-dialog" class="jira-dialog box-shadow popup-width-custom" data-aui-modal="true" data-aui-remove-on-hide="true" style="width: 810px; margin-left: -406px; margin-top: -434.5px;">
<div class="jira-dialog-heading">
<h2 title="Developer-only comment on: $issueKey">Developer-only comment on: $issueKey</h2>
</div>
<div class="field-group aui-field-wikiedit comment-input">
<div class="jira-wikifield" field-id="comment" renderer-type="atlassian-wiki-renderer" issue-key="$issueKey" resolved="">
<div class="wiki-edit">
<div id="comment-wiki-edit" class="wiki-edit-content">
<textarea class="textarea long-field wiki-textfield mentionable wiki-editor-initialised" cols="60" id="devCommentText" name="devCommentText" wrap="virtual" data-issuekey="$issueKey" resolved="" style="min-height: 174px; max-height: 540px;"></textarea>
<div class="rte-container">
<rich-editor contenteditable="true"></rich-editor>
</div>
</div>
</div>
<div class="field-tools">
<button class="jira-icon-button fullscreen wiki-preview" id="comment-preview_link" type="button">
</button>
</div>
</div>
<div class="save-options wiki-button-bar">
</div>
<div class="buttons-container form-footer">
<div class="buttons"><span class="icon throbber"></span><button id="dev-only-dialog-submit" class="aui-button">Submit</button><button id="dev-only-dialog-cancel" class="aui-button aui-button-link">Cancel</button>
</div>
</div>
</div>
<script>
AJS.dialog2("#dev-only-dialog").on("show", function() {
document.getElementById('devCommentText').focus();
});
AJS.\$("#dev-only-dialog-submit").click(function(e) {
var text = AJS.\$("#devCommentText").val();
var comment = {"body": text,
"visibility": {
"type": "role",
"value": "Developers"
}};
AJS.\$.ajaxSetup({
baseUrl: '""" + baseUrl + """'
});
AJS.\$("#dev-only-dialog-submit").before('<span class="aui-icon aui-icon-wait">Submitting...</span>');
AJS.\$("#dev-only-dialog-submit").prop("disabled", true);
AJS.\$.ajax({
url : '/rest/api/2/issue/""" + issueId + """/comment',
headers: { "Content-Type" : "application/json" },
type: 'POST',
data : JSON.stringify(comment),
cache : false,
success: function(response){
JIRA.trigger(JIRA.Events.REFRESH_ISSUE_PAGE, [JIRA.Issue.getIssueId()]);
JIRA.trigger("GH.RapidBoard.causeBoardReload");
var myFlag = AJS.flag({
type: 'success',
title: 'Comment added successfully',
close: 'auto'
});
AJS.dialog2("#dev-only-dialog").remove();
},
error: function(response){
console.log(JSON.stringify(response));
var myFlag = AJS.flag({
type: 'error',
title: 'Oh no!',
body: JSON.parse(response.responseText).errors.comment,
close: 'manual'
});
AJS.dialog2("#dev-only-dialog").remove();
}
});
});
AJS.\$("#dev-only-dialog-cancel").click(function(e) {
AJS.dialog2("#dev-only-dialog").remove();
});
</script>
</div>
"""
Response.ok().type(MediaType.TEXT_HTML).entity(dialog.toString()).build()
}
To change the target project role, just replace the two 'Developers' references.
Also, remember to point your 'Run code and display a dialog' Custom Web Item at
'/rest/scriptrunner/latest/custom/addDeveloperPermissionedCommentDialog?issueId=${issue.id}&issueKey=${issue.getKey()}'
We placed our button/fragment in the 'operations-top-level' section, weighted below the 'Assign' action.
Finally, we have this working as a Web Item on ScriptRunner 5.3.7, but on older versions, we were using a Web Item Provider like this:
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.project.Project
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.util.velocity.VelocityRequestContextFactory
import com.atlassian.jira.security.roles.ProjectRoleManager
import com.atlassian.jira.security.roles.ProjectRoleActors
import com.atlassian.plugin.web.api.model.WebFragmentBuilder
Issue issue = (Issue)context.get('issue')
Project project = (Project)context.get('project')
def projects = ['<Your project KEY here>']
if (!projects.find{it == project?.getKey()})
return
def velocityRequestContextFactory = ComponentAccessor.getComponent(VelocityRequestContextFactory)
def baseUrl = velocityRequestContextFactory.getJiraVelocityRequestContext().getBaseUrl()
def currentUser = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
ProjectRoleManager projectRoleManager = ComponentAccessor.getComponent(ProjectRoleManager)
ProjectRoleActors developers = projectRoleManager.getProjectRoleActors(projectRoleManager.getProjectRole("Developers"), issue.getProjectObject())
if(developers.contains(currentUser)
{
def fragments = []
fragments << new WebFragmentBuilder(50) //(50) is the weight
.id("dialog-provider")
.label("Dev-only comment")
.title("Dev-only comment")
.styleClass("sr-trigger-dialog")
.webItem("")
.url("${baseUrl}/rest/scriptrunner/latest/custom/addDeveloperPermissionedCommentDialog?issueId=${issue.id}&issueKey=${issue.getKey()}")
.build()
return fragments
}
Good luck, and cheers!
Josh
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
So, I've been looking into this, and I've not quite been able to divine how to use Jira's built-in rich text editor.
I'm pretty sure that the basics are:
1) Create a textarea element in your markup
2) Run some javascript that will invoke Jira's rich text editor
Obviously, the particulars of #2 evade me as yet.
https://developer.atlassian.com/jiradev/jira-platform/guides/rich-text-editor
I expect it'll be something like
require("jira/editor/somemodule").init( document.querySelector('textarea#mySpecialTextArea') );
...but I've not yet divined the right module or method, even after digging into Jira's source.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
There's nothing in Atlassian's AUI docs, which makes me think it's not a simple matter of adding a class or ID to the text area. https://docs.atlassian.com/aui/latest/docs/forms.html
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Online forums and learning are now in one easy-to-use experience.
By continuing, you accept the updated Community Terms of Use and acknowledge the Privacy Policy. Your public name, photo, and achievements may be publicly visible and available in search engines.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.