Examples of using variables that are available for use in dynamic templates.
- Velocity Template Basics
- Data Access Methods
- Message Display Configuration
- Context and State Variables
- Utility Services
- Field Validation Examples
- Advanced Features
- Real-World Example Scenarios
- Priority-Based Warning Messages
- Priority-Dependent Validation
- Assignee Information with External Links
- Issue Type-Specific Messages
- Component-Based Team Assignments
- Due Date Warnings
- Fix Version Validation
- Issue Type Requirements
- Budget Threshold Alerts
- Component-Specific Reminders
- Attachment Type Detection
- Additional variables
You can see the official user guide for velocity here.
Velocity Template Basics
Default Values for Variables
When working with variables that might be null or undefined, you can use these approaches to provide default values:
Silent Reference Notation
$!variable
This displays an empty string if the variable is null, instead of showing the literal $variable
.
Conditional Statement with Default Value
#if($variable)$variable#else Default value #end
This checks if the variable exists and has a value, otherwise displays the default.
Macro for Reusable Default Values
#macro(default $value $defaultValue)#if($value)$value#else$defaultValue#end#end
Add this macro at the beginning of your template, then use it like:
#default($issue.assignee "Not assigned")
#default($cfValues.getFromForm("Custom Field") "No value set")
Examples:
## Silent reference - shows empty string if assignee is null
Assignee: $!issue.assignee.displayName
## Conditional with default text
Assignee: #if($issue.assignee)$issue.assignee.displayName#else Not assigned #end
## Using macro (add macro definition at top of template)
#macro(default $value $defaultValue)#if($value)$value#else$defaultValue#end#end
Assignee: #default($issue.assignee.displayName "Not assigned")
Priority: #default($formIssue.priority.name "No priority set")
Core Velocity Syntax
How to set the value of variable?
#set( $foo = "Velocity" )
Hello $foo World!
Conditionals
#set( $foo = "Velocity" )
Hello
#if( $foo )
<strong>Velocity!</strong>
#end
World!
Loops
<ul>
#foreach( $product in $allProducts )
<li>$product</li>
#end
</ul>
Comments
## This is a single line comment.
#*
Thus begins a multi-line comment. Online visitors won't
see this text because the Velocity Templating Engine will
ignore it.
*#
Data Access Methods
Form Data Access
How to use $form? Get raw data from issue screen
$form.summary <br>
$form.customfield_10400
How to use $formIssue? Get valid data from the issue screen
$formIssue.summary <br>
$formIssue.priority.name <br>
#if($formIssue.assignee)
$formIssue.assignee.displayName<br>
#else
Unassigned<br>
#end
Custom Field Values ($cfValues)
The CFValues utility provides comprehensive methods for accessing custom field values from issues and forms.
How to get the value of a custom field for an $issue
$cfValues.get(10100)<br>
$cfValues.get("customfield_10100")<br>
$cfValues.get("Custom field name")<br>
$cfValues.getOrDefault(10100, "Default value")<br>
$cfValues.getOrDefault("customfield_10100", 123)<br>
$cfValues.getOrDefault("Custom field name", $issue.assignee)<br>
## Use to get value for linked issue
$cfValues.get($linkedIssue, 10100)<br>
$cfValues.getOrDefault($linkedIssue, "customfield_10100", "Default value")<br>
$cfValues.getOrDefault($linkedIssue, "Custom field name", 123)<br>
How to get the value of a custom field for an $formIssue
$cfValues.getFromForm(10100)<br>
$cfValues.getFromForm("customfield_10100")<br>
$cfValues.getFromForm("Custom field name")<br>
$cfValues.getFromFormOrDefault(10100, "Default value")<br>
$cfValues.getFromFormOrDefault("customfield_10100", 123)<br>
$cfValues.getFromFormOrDefault("Custom field name", $issue.assignee)<br>
How to get raw values from form (without type conversion)
The getRawCFValueFromForm
methods return the raw value from the form without any type conversion:
## Get raw value by field ID as number
#set($rawValue = $cfValues.getRawCFValueFromForm(10100))
## Get raw value by field ID as string
#set($rawValue = $cfValues.getRawCFValueFromForm("customfield_10100"))
## Example: Working with raw JSON data from a text field
#set($jsonData = $cfValues.getRawCFValueFromForm("customfield_10200"))
#if($jsonData)
## Parse JSON data
#set($parsedData = $JSON.parse($jsonData))
#if($parsedData)
Configuration loaded: $parsedData.get("configName")
#end
#end
## Example: Checking raw form values before type conversion
#set($rawSelectValue = $cfValues.getRawCFValueFromForm(10300))
#if($rawSelectValue)
Raw select field value (option ID): $rawSelectValue
## Compare with converted value
#set($convertedValue = $cfValues.getFromForm(10300))
Converted value (option object): $convertedValue.value
#end
Issue Field Access
How to use issue type
#if($formIssue.issueType.name == 'Task')
message for 'Task'
#elseif($formIssue.issueType.name == 'Bug')
message for 'Bug'
#end
or
#if($formIssue.issueType.id == 10000)
message for 'Task'
#elseif($formIssue.issueType.id == 10001)
message for 'Bug'
#end
or
#if($issue.issueType.name == 'Task')
message for 'Task'
#elseif($issue.issueType.name == 'Bug')
message for 'Bug'
#end
($issue - this variable is not defined on the creation screen)
How to use issue status
#if($issue.status.name == 'To Do')
message for 'To Do' status
#elseif($issue.status.name == 'In progress')
message for 'In progress' status
#end
or
#if($issue.status.id == 10000)
message for 'To Do' status
#elseif($issue.status.id == 10001)
message for 'In progress' status
#end
Get parent issue
$formIssue.parentObject.key
$issue.parentObject.key
#if($formIssue.parentObject)
$cfValues.get($formIssue.parentObject, "Impact").value ## impact - custom select field
#end
Message Display Configuration
How to use $fieldDisplayConfig? How to change the display of a message?
see Java doc $fieldDisplayConfig
$fieldDisplayConfig.setAsFlag(true) ## true, false
$fieldDisplayConfig.setMessageType("info") ## "info", "error", "success", "warning", "change", "SIMPLE_VIEW"
$fieldDisplayConfig.setTitle("New title")
$fieldDisplayConfig.setShowForFieldId("summary") ## "customfield_10100"
$fieldDisplayConfig.setInsert("before") ## "append", "prepend", "before", "after"
$fieldDisplayConfig.setHidden(true)
How to hide a message?
see Java doc $fieldDisplayConfig
#set ($optionCustomFieldValue = $cfValues.getFromForm("Option custom field name"))
#if($optionCustomFieldValue.value == "option 1" )
$fieldDisplayConfig.setTitle("New title for option 1")
Body text for option 1<br>
#elseif($optionCustomFieldValue.value == "option 2" )
#set ($titleForOption2 = "New title for " + $optionCustomFieldValue.value)
$fieldDisplayConfig.setTitle($titleForOption2)
Body text for option 2<br>
#else
$fieldDisplayConfig.setHidden(true)##hide
#end
How to change message colour?
see Java doc $fieldDisplayConfig
$fieldDisplayConfig.setMessageType("custom") ##require for custom colors
#set($r = $mathTool.random(0,255))
#set($g = $mathTool.random(0,255))
#set($b = $mathTool.random(0,255))
$fieldDisplayConfig.setBackgroundColor("rgb($r,$g,$b)") ## color as hex "#FFFFFF", "rgb(255,255,255)", "rgba(255,255,255, 1)"
#set($r = $mathTool.random(0,255))
#set($g = $mathTool.random(0,255))
#set($b = $mathTool.random(0,255))
$fieldDisplayConfig.setTextColor("rgb($r,$g,$b)")
#set($r = $mathTool.random(0,255))
#set($g = $mathTool.random(0,255))
#set($b = $mathTool.random(0,255))
$fieldDisplayConfig.setIconColor("rgb($r,$g,$b)")
$issue.key: $issue.summary
Context and State Variables
How to use $context, $transitionId and $transitionName?
#if($context == "TRANSITION")
is transitionId 31 ? #if($transitionId == 31) Yes #else No #end <br>
is transitionName "In Progress" ? #if($transitionName == "In Progress") Yes #else No #end<br>
#end
How to check how long ago the transition was made? (How to use $secAfterLastTransition)
$fieldDisplayConfig.setHidden(true)##hide message by default (Including cases when there were no transitions yet.)
#if($secAfterLastTransition && $secAfterLastTransition < 1*60)##We check that less than 1 minute has passed since the last transition (the time is indicated as a number of seconds)
$fieldDisplayConfig.setHidden(false)## Show a message if the transition was not long ago. (< 1 min ago)
##message edit here
data:<br>
$secAfterLastTransition<br>
$previousStatusId <br>
$previousStatusName <br>
#end
How to use and check linked issues?
The Links utility provides comprehensive methods for working with issue links. All methods can be called with or without parameters to filter by link type.
Available methods:
Outward Issues and Links:
getOutwardIssues()
- Get all outward linked issuesgetOutwardIssues(String linkTypeName)
- Get outward issues by link type namegetOutwardIssues(long linkTypeId)
- Get outward issues by link type IDgetOutwardIssuesFromForm()
- Get outward issues including form datagetOutwardIssuesFromForm(String linkTypeName)
- Get outward issues from form by type namegetOutwardIssuesFromForm(long linkTypeId)
- Get outward issues from form by type IDgetRawOutwardIssuesFromForm()
- Get only form data outward issuesgetRawOutwardIssuesFromForm(String linkTypeName)
- Get raw form outward issues by typegetRawOutwardIssuesFromForm(long linkTypeId)
- Get raw form outward issues by type IDgetOutwardLinks()
- Get all outward issue linksgetOutwardLinks(String linkTypeName)
- Get outward links by type namegetOutwardLinks(long linkTypeId)
- Get outward links by type ID
Inward Issues and Links:
getInwardIssues()
- Get all inward linked issuesgetInwardIssues(String linkTypeName)
- Get inward issues by link type namegetInwardIssues(long linkTypeId)
- Get inward issues by link type IDgetInwardIssuesFromForm()
- Get inward issues including form datagetInwardIssuesFromForm(String linkTypeName)
- Get inward issues from form by type namegetInwardIssuesFromForm(long linkTypeId)
- Get inward issues from form by type IDgetRawInwardIssuesFromForm()
- Get only form data inward issuesgetRawInwardIssuesFromForm(String linkTypeName)
- Get raw form inward issues by typegetRawInwardIssuesFromForm(long linkTypeId)
- Get raw form inward issues by type IDgetInwardLinks()
- Get all inward issue linksgetInwardLinks(String linkTypeName)
- Get inward links by type namegetInwardLinks(long linkTypeId)
- Get inward links by type ID
Examples:
Basic usage - check blocked issues:
#set ($blockedIssues = $links.getOutwardIssues("blocked"))
#if($blockedIssues.size() > 0)
#foreach($blockedIssue in $blockedIssues)
$blockedIssue.status.name $cfValues.getOrDefault($blockedIssue, "developer custom field", "hasn't developer")<br>
#end
#end
Check all linked issues (both directions):
## Get all outward issues
#set($outwardIssues = $links.getOutwardIssues())
#set($inwardIssues = $links.getInwardIssues())
#if($outwardIssues.size() > 0 || $inwardIssues.size() > 0)
<h4>Linked Issues:</h4>
#if($outwardIssues.size() > 0)
<h5>This issue links to:</h5>
<ul>
#foreach($issue in $outwardIssues)
<li>$issue.key - $issue.summary ($issue.status.name)</li>
#end
</ul>
#end
#if($inwardIssues.size() > 0)
<h5>Issues linking to this:</h5>
<ul>
#foreach($issue in $inwardIssues)
<li>$issue.key - $issue.summary ($issue.status.name)</li>
#end
</ul>
#end
#else
<p>No linked issues found.</p>
#end
Work with specific link types:
## Check if issue is blocked
#set($blockers = $links.getInwardIssues("Blocks"))
#if($blockers.size() > 0)
<div style="background: #ffebee; padding: 10px;">
<h4>⛔ This issue is blocked by:</h4>
#foreach($blocker in $blockers)
<p>$blocker.key - $blocker.summary
#if($blocker.status.name != "Done")
<span style="color: red;">(Still Open)</span>
#end
</p>
#end
</div>
#end
## Check dependencies
#set($dependencies = $links.getOutwardIssues("Dependency"))
#if($dependencies.size() > 0)
<h4>Dependencies:</h4>
#foreach($dep in $dependencies)
- $dep.key: $dep.summary (Status: $dep.status.name)<br>
#end
#end
Working with form data (for create/edit screens):
## Get links including unsaved changes from form
#set($allOutwardIssues = $links.getOutwardIssuesFromForm())
#if($allOutwardIssues.size() > 0)
<p>Total linked issues (including unsaved): $allOutwardIssues.size()</p>
#end
## Get only new links from form (not yet saved)
#set($newLinks = $links.getRawOutwardIssuesFromForm())
#if($newLinks.size() > 0)
<p style="color: orange;">⚠️ You are adding $newLinks.size() new link(s)</p>
#end
Utility Services
JQL Service ($jqlService)
- see Java doc $jqlService
- linked Post: How to use linked issues and JQL results in Dynamic templates?
Available methods:
Search methods:
getIssuesByJQL(String jql, int maxCount)
- Search issues with current user’s permissions
Count methods:
getIssueCountByJQL(String jql)
- Count issues with current user’s permissions
How to use JQL in message?
$jqlService.getIssuesByJQL("priority = $formIssue.priority.name ORDER BY Key DESC", 10)
You can use current issue in jql for conditions:
#if($jqlService.getIssueCountByJQL("key = $issue.key and updated < startOfDay(-2)") > 0)
The problem has not been updated for more than two days.
#end
Advanced JQL examples:
Count issues for statistics:
#set($criticalCount = $jqlService.getIssueCountByJQL("project = $issue.projectObject.key AND priority = Critical AND status != Done"))
#set($highCount = $jqlService.getIssueCountByJQL("project = $issue.projectObject.key AND priority = High AND status != Done"))
#if($criticalCount > 0 || $highCount > 0)
<div style="background: #fff3cd; padding: 10px;">
<h4>⚠️ Project has high priority issues:</h4>
<p>Critical: $criticalCount issues</p>
<p>High: $highCount issues</p>
</div>
#end
How to show fields for issues by JQL in message?
- see Java doc $jqlService
- linked Post: How to use linked issues and JQL results in Dynamic templates?
$issueFieldRender.getAsTableHtml(
$jqlService.getIssuesByJQL("priority = $formIssue.priority.name ORDER BY Key DESC", 5),
"issue key", "Priority", "Assignee", "customfield_10110", "Product categorization", "10110"
)
Issue Field Renderer ($issueFieldRender)
see Java doc $issueFieldRender
The Issue Field Renderer provides methods for rendering issue fields and formatting data.
Available methods:
getFieldValueHtml(Issue issue, long customFieldId)
- Get HTML rendered value of custom field by IDgetFieldValueHtml(Issue issue, String field)
- Get HTML rendered value of field by name/IDgetFieldName(long customFieldId)
- Get the name of a custom field by its IDgetFieldName(String fieldForView)
- Get the name of a field by its string identifiergetAsTableHtml(List<Issue> issues, String... fields)
- Render issues as HTML tablegetAsTableHtml(Issue issue, String... fields)
- Render single issue fields as HTML tablegetJQLAsTableHtml(String jql, int maxCount, String... fields)
- Execute JQL and render results as tablereplaceNewlineCharactersForHtml(String string)
- Convert newlines to HTML breaksdateFormat(Date date, String format)
- Format date with specified pattern
How to show fields from linked issues (also on Service Desk Portal)?
This example uses a different our plugin: Display linked issues 1) Create and configure field “Linked issues”:
2) After creating the “linked issues” field, you can display it in the message body:
$issueFieldRender.getFieldValueHtml($issue,"customfield_10101")
3) Result:
![]() |
![]() |
How to display the date in the correct format?
see Java doc $issueFieldRender
$issueFieldRender.dateFormat($formIssue.created, "dd-MMM-yyy HH:mm")
<br/>
$issueFieldRender.dateFormat($formIssue.created, "dd-MM-yyyy")
How to show the multiline value of a text field?
see Java doc $issueFieldRender
<b>original value of issueForm field "description":</b><br/>
$formIssue.description
<br/>
<b>After formating for better look:</b><br/>
$issueFieldRender.replaceNewlineCharactersForHtml($formIssue.description)
<br/
<br/>
<b>Original value of issueForm custom field "Text Field (multi-line)":</b><br/> ## customfield_10102
$cfValues.getFromForm("customfield_10102")
<br/>
<br/>
<b>After formating for better look:</b><br/>
$issueFieldRender.replaceNewlineCharactersForHtml($cfValues.getFromForm("Text Field (multi-line)"))
<hr/>
<b>HTML render available for any field of issue:</b><br/>
<b>Description:</b><br/>
$issueFieldRender.getFieldValueHtml($issue, "description")
<br/>
<b>Text Field (multi-line):</b><br/>
$issueFieldRender.getFieldValueHtml($issue, "customfield_10102")
How to get field names dynamically?
## Get custom field name by ID
#set($fieldName = $issueFieldRender.getFieldName(10100))
<p>Field 10100 is named: $fieldName</p>
## Get field name by field key
#set($summaryName = $issueFieldRender.getFieldName("summary"))
<p>Summary field display name: $summaryName</p>
How to render JQL results directly as a table?
## Execute JQL and display results as table
$issueFieldRender.getJQLAsTableHtml(
"project = $issue.projectObject.key AND created >= -7d",
10,
"Key", "Summary", "Status", "Assignee", "Created"
)
## Display single issue data as table
$issueFieldRender.getAsTableHtml(
$issue,
"Summary", "Status", "Priority", "Assignee", "customfield_10100"
)
Field Validation Examples
Priority Validation
#if($formIssue.priority.name == "Blocker")
$fieldDisplayConfig.setMessageType("error")
State the reason for the Blocker priority. ##Message text
#elseif($formIssue.priority.name == "High")
$fieldDisplayConfig.setMessageType("warning")
State the reason for the High priority. ##Message text
#end
Component Validation
#foreach($component in $formIssue.components)
#if($component.name == "Test component")
You have selected a test component ##Message text
#end
#end
Summary Validation
#if($formIssue.summary.contains("PROJECTKEY"))
message contains PROJECTKEY<br> ##Message text
#end
#if($formIssue.summary.matches("(.*)PROJECTKEY(.*)"))
message contains PROJECTKEY (cheked by regex)<br> ##Message text
#end
#if($formIssue.summary.length() < 10)
Short summary<br> ##Message text
#end
Description and User Picker Validation
#if(!$form.description || $form.description.trim() == "" || !$cfValues.getFromForm("User Picker (single user)"))
You need to set data in the system description field and specify the user in the custom field "User Picker (single user)" ##Message text
#end
Number Field Validation
#if($cfValues.getFromForm("Number") > 10)
number > 10 ##Message text
#end
Select Field Validation
#if($cfValues.getFromForm("Radio").optionId == 10102)
Selected option with Id 10102<br> ##Message text
#end
#if($cfValues.getFromForm("Single select field").name == "yes")
Selected option with name "yes"<br> ##Message text
#end
#if($form.customfield_10202.indexOf("10301") > -1)
"Select List (multiple choices)" contains otpion with id 10301(option2) ##Message text
#end
Advanced Features
Multi-language Messages
#if($language == "es")
mensaje en español<br>
línea 2
#elseif($language == "fr")
message en français<br>
ligne 2
#else
message in english<br>
line 2<br>
#end
Cascade Select Field
Working with $cfValues:
1 - $cfValues.getFromForm(10101)<br>
2 - $cfValues.getFromForm("customfield_10101")<br>
3 - $cfValues.getFromForm("customfield_10101").getOptionId()<br>
4 - $cfValues.getFromForm("customfield_10101").getValue()<br>
5 - $cfValues.getFromForm("customfield_10101:1")<br>
6 - $cfValues.getFromForm("customfield_10101:1").getOptionId()<br>
7 - $cfValues.getFromForm("customfield_10101:1").getValue()<br>
8 - $cfValues.getFromForm("cascade test")<br>
9 - $cfValues.getFromForm("cascade test:1")<br>
<br>
10 - $cfValues.getFromFormOrDefault("customfield_10101", "defaultOption")<br>
11 - $cfValues.getFromFormOrDefault("customfield_10101:1", "defaultOption")<br>
12 - $cfValues.getFromFormOrDefault("cascade test", "defaultOption")<br>
13 - $cfValues.getFromFormOrDefault("cascade test:1", "defaultOption")<br>
14 - $cfValues.getFromFormOrDefault("cascade test1:1", "defaultOption")<br>
15 - $cfValues.getFromFormOrDefault("cascade test1:12", "defaultOption")<br>
<br>
16 - $cfValues.get(10101)<br>
17 - $cfValues.get(10101).get(null)<br>
18 - $cfValues.get(10101).get("1")<br>
19 - $cfValues.get("customfield_10101")<br>
20 - $cfValues.get("customfield_10101").get(null).getValue()<br>
21 - $cfValues.get("customfield_10101").get("1").getValue()<br>
22 - $cfValues.get("customfield_10101").get(null).getOptionId()<br>
23 - $cfValues.get("customfield_10101").get("1").getOptionId()<br>
<br>
24 - $cfValues.getOrDefault("customfield_10101", "defaultValue")<br>
25 - $cfValues.getOrDefault("customfield_10101", 10000).get(null).getOptionId()<br>
26 - $cfValues.getOrDefault("customfield_10101", 10000).get("1").getOptionId()<br>
27 - $cfValues.getOrDefault("cascade test", "defaultOption")<br>
28 - $cfValues.getOrDefault("cascade test", "defaultOption").get(null).getValue()<br>
29 - $cfValues.getOrDefault("cascade test", "defaultOption").get("1").getValue()<br>
30 - $cfValues.getOrDefault("cascade test123123", "defaultOption")<br>
31 - $cfValues.getOrDefault("cascade test1:12", 10000)<br>
Working with $form object directly:
When working with cascade select fields through the $form
object, you access the raw option IDs:
## Get parent option ID from form
#set($parentOptionId = $form.get("customfield_10101"))
#if($parentOptionId)
Parent option ID: $parentOptionId
#end
## Get child option ID from form
#set($childOptionId = $form.get("customfield_10101:1"))
#if($childOptionId)
Child option ID: $childOptionId
#end
## Example: Conditional logic based on cascade select values
#if($form.get("customfield_10101") == "10000")
## Parent option with ID 10000 is selected
#if($form.get("customfield_10101:1") == "10100")
## Child option with ID 10100 is selected
<p>You selected: Category A -> Subcategory 1</p>
#elseif($form.get("customfield_10101:1") == "10101")
<p>You selected: Category A -> Subcategory 2</p>
#end
#elseif($form.get("customfield_10101") == "10001")
## Parent option with ID 10001 is selected
<p>You selected Category B</p>
#end
## Example: Validate cascade select combinations
#set($parentId = $form.get("customfield_10101"))
#set($childId = $form.get("customfield_10101:1"))
#if($parentId == "10000" && (!$childId || $childId == ""))
<div style="color: red;">
⚠️ Please select a subcategory for Category A
</div>
#end
## Example: Dynamic message based on cascade selection
#set($cascadeMessages = {
"10000": {
"10100": "Development -> Frontend: Please follow React guidelines",
"10101": "Development -> Backend: Ensure API documentation is updated",
"10102": "Development -> Database: Include migration scripts"
},
"10001": {
"10200": "Testing -> Manual: Create test cases in TestRail",
"10201": "Testing -> Automated: Add to regression suite"
}
})
#set($parentId = $form.get("customfield_10101"))
#set($childId = $form.get("customfield_10101:1"))
#if($parentId && $childId)
#set($message = $cascadeMessages.get($parentId).get($childId))
#if($message)
<div style="background: #e3f2fd; padding: 10px;">
📌 $message
</div>
#end
#end
## Example: Get all form values for debugging
#foreach($key in $form.keys())
#if($key.startsWith("customfield_10101"))
Form key: $key = $form.get($key)<br>
#end
#end
Conditions:
#if($cfValues.getFromForm(10101) == "parent1")
message<br>
#end
#if($cfValues.getOrDefault("cascade test", "defaultOption").get("1").getValue() == "child1")
message2<br>
#end
#if($cfValues.getFromForm("customfield_10101").getOptionId() == 10000)
message3<br>
#end
Insight Service ($insightService)
$insightService.findObjects("key = TEST-1", 1) ##find (1) object by IQL
<br>
$insightService.findObjects("key = TEST-1", 1).get(0).getObjectAttributeBeans() ##get all attribute's ids from first object
<br>
$insightService.getIQLAsTableHtml("key = TEST-1", 1, "Key", "Created", "Name", "Updated") ## use attribute's names
<br>
$insightService.getIQLAsTableHtml("key = TEST-1", 1, 3, 2, 1, 4) ## use attribute's ids
<br>
$insightService.getIQLAsTableHtml("key = TEST-1", 1, 3, 2, 1, 4) ## use attribute's ids
<br>
search by objectId:<br>
$insightService.getIQLAsTableHtml("objectId = 1", 1, "Key", "Created", "Name", "Updated") ## search by objectId and display attributes by names
<br>
Work with insight object custom field:<br>
#if($cfValues.getFromForm(10200))#insight custom field. If value isn't empty IQL will use id from insight object(value).
$insightService.getIQLAsTableHtml("objectId = $cfValues.getFromForm(10200).id", 1, "Key", "Created", "Name", "Updated") ## search by objectId and display attributes by names
#end
User Group Service ($userGroupService)
see Java doc $userGroupService
Get user by username:
#set($user = $userGroupService.getUserByName("john.doe"))
#if($user)
User found: $user.displayName ($user.emailAddress)
#else
User not found
#end
Result (if user exists):
User found: John Doe (john.doe@company.com)
Result (if user doesn’t exist):
User not found
Check if user is in group:
## Check if specific user is in administrators group
#if($userGroupService.isUserInGroup("john.doe", "jira-administrators"))
User john.doe is an administrator
#else
User john.doe is not an administrator
#end
## Check if current user is in specific group
#if($userGroupService.isUserInGroup($currentUser, "developers"))
Current user is a developer
#else
Current user is not a developer
#end
Result (if john.doe is admin and current user is developer):
User john.doe is an administrator
Current user is a developer
Result (if john.doe is not admin and current user is not developer):
User john.doe is not an administrator
Current user is not a developer
Show different messages based on user group membership:
#if($userGroupService.isUserInGroup($currentUser, "jira-administrators"))
$fieldDisplayConfig.setMessageType("info")
<strong>Administrator Access:</strong> You have full system privileges.
#elseif($userGroupService.isUserInGroup($currentUser, "project-managers"))
$fieldDisplayConfig.setMessageType("success")
<strong>Project Manager:</strong> You can manage project settings and user assignments.
#elseif($userGroupService.isUserInGroup($currentUser, "developers"))
$fieldDisplayConfig.setMessageType("warning")
<strong>Developer Access:</strong> Please follow coding standards and review guidelines.
#else
$fieldDisplayConfig.setMessageType("error")
<strong>Limited Access:</strong> Contact your administrator for additional permissions.
#end
Result (for administrator):
Result (for project manager):
Result (for developer):
Result (for regular user):
Validate assignee group membership:
#if($formIssue.assignee)
#if($userGroupService.isUserInGroup($formIssue.assignee, "developers"))
✓ Assignee is a developer - ready for development
#elseif($userGroupService.isUserInGroup($formIssue.assignee, "qa-team"))
✓ Assignee is a QA team member - ready for testing
#else
⚠️ Warning: Assignee is not in development or QA team
#end
#else
❌ No assignee selected
#end
Result (assignee is developer):
✓ Assignee is a developer - ready for development
Result (assignee is QA team member):
✓ Assignee is a QA team member - ready for testing
Result (assignee is in other group):
⚠️ Warning: Assignee is not in development or QA team
Result (no assignee):
❌ No assignee selected
Dynamic user mentions based on group:
#set($supportUsers = ["admin", "support.manager", "help.desk"])
#set($supportTeamMembers = [])
#foreach($username in $supportUsers)
#if($userGroupService.isUserInGroup($username, "jira-servicedesk-users"))
#set($user = $userGroupService.getUserByName($username))
#if($user)
$supportTeamMembers.add($user)
#end
#end
#end
#if($supportTeamMembers.size() > 0)
<p>Available support team members:</p>
<ul>
#foreach($member in $supportTeamMembers)
<li>$member.displayName - $member.emailAddress</li>
#end
</ul>
#else
<p>No support team members are currently available.</p>
#end
Result (when support members are found):
Available support team members:
- System Administrator - admin@company.com
- Support Manager - support.manager@company.com
- Help Desk - help.desk@company.com
Result (when no support members are found):
No support team members are currently available.
Cast Utility ($cast)
The Cast utility provides methods for safe type conversion in Velocity templates.
Available methods:
toInteger(Object object) Converts an object to Integer. Returns null if conversion fails.
$cast.toInteger("123") ## returns 123
$cast.toInteger("abc") ## returns null
$cast.toInteger(null) ## returns null
toLong(Object object) Converts an object to Long. Returns null if conversion fails.
$cast.toLong("9999999999") ## returns 9999999999L
$cast.toLong("abc") ## returns null
$cast.toLong(null) ## returns null
toString(Object object) Converts an object to String. Returns null if object is null.
$cast.toString(123) ## returns "123"
$cast.toString($issue) ## returns string representation
$cast.toString(null) ## returns null
Example: Dynamic messages based on issue type and custom field
#set ($messages = {
"Story" : {
10000: "Message for story with option id 10000",
10001: "Message for story with option id 10001"
} ,
"Task" : {
10000: "Message for task with option id 10000",
10001: "Message for task with option id 10001"
}
})
#set($message = $messages.get($formIssue.issueType.name).get(
$cast.toInteger($cfValues.getFromForm("customfield_10200").optionId)
))
#if($message)
$message
#else
$fieldDisplayConfig.setHidden(true)##hide
#end
JSON Parser ($JSON)
The JSON utility provides JSON parsing capabilities in Velocity templates.
Available methods:
parse(String jsonString) Parses a JSON string and returns a JSONObject. Returns null if parsing fails.
#set($jsonString = '{"name": "John", "age": 30, "active": true}')
#set($jsonObject = $JSON.parse($jsonString))
#if($jsonObject)
Name: $jsonObject.get("name")
Age: $jsonObject.get("age")
Active: $jsonObject.get("active")
#else
Failed to parse JSON
#end
Example: Working with JSON data from custom fields
## Assuming a text field contains JSON data
#set($jsonData = $cfValues.get("JSON Data Field"))
#if($jsonData)
#set($parsedData = $JSON.parse($jsonData))
#if($parsedData)
<h4>Parsed Configuration:</h4>
<ul>
#foreach($key in $parsedData.keys())
<li>$key: $parsedData.get($key)</li>
#end
</ul>
#else
<p style="color: red;">Invalid JSON format in custom field</p>
#end
#end
Permission Helper ($permissionHelper)
The Permission Helper provides methods to check user permissions in templates. This utility is only available in non-delegated mode.
Available methods:
hasPermission(String permission) Checks if the current user has the specified permission for the current issue.
#if($permissionHelper.hasPermission("EDIT_ISSUES"))
You can edit this issue
#else
You cannot edit this issue
#end
hasPermission(String permission, ApplicationUser user) Checks if the specified user has the permission for the current issue.
#if($permissionHelper.hasPermission("ASSIGN_ISSUES", $formIssue.assignee))
The assignee can assign issues
#end
hasPermission(String permission, Issue issue) Checks if the current user has the permission for the specified issue.
#set($parentIssue = $formIssue.parentObject)
#if($parentIssue && $permissionHelper.hasPermission("VIEW_ISSUES", $parentIssue))
You can view the parent issue
#end
hasPermission(String permission, Issue issue, ApplicationUser user) Checks if the specified user has the permission for the specified issue.
#if($permissionHelper.hasPermission("COMMENT_ISSUES", $linkedIssue, $currentUser))
You can comment on the linked issue
#end
Common permission types:
- VIEW_ISSUES - View issues
- CREATE_ISSUES - Create issues
- EDIT_ISSUES - Edit issues
- ASSIGN_ISSUES - Assign issues
- RESOLVE_ISSUES - Resolve issues
- CLOSE_ISSUES - Close issues
- COMMENT_ISSUES - Add comments
- DELETE_ISSUES - Delete issues
- WORK_ON_ISSUES - Work on issues
- LINK_ISSUES - Link issues
Example: Display content based on permissions
#if($permissionHelper.hasPermission("EDIT_ISSUES"))
<div style="background: #e8f5e8; padding: 10px;">
<h4>✏️ Editor Actions</h4>
<p>You have permission to edit this issue.</p>
<p>Remember to follow the editing guidelines.</p>
</div>
#elseif($permissionHelper.hasPermission("COMMENT_ISSUES"))
<div style="background: #fff3cd; padding: 10px;">
<h4>💬 Commenter Access</h4>
<p>You can add comments but cannot edit the issue.</p>
<p>Please use comments for suggestions.</p>
</div>
#else
<div style="background: #f8d7da; padding: 10px;">
<h4>👁️ View Only</h4>
<p>You have read-only access to this issue.</p>
</div>
#end
Component Accessor ($ComponentAccessor)
The Component Accessor provides access to various Jira components and services. This powerful utility is only available in non-delegated mode.
Common usage examples:
Get Jira managers and services:
## Get various Jira managers
#set($projectManager = $ComponentAccessor.getProjectManager())
#set($userManager = $ComponentAccessor.getUserManager())
#set($groupManager = $ComponentAccessor.getGroupManager())
#set($customFieldManager = $ComponentAccessor.getCustomFieldManager())
#set($versionManager = $ComponentAccessor.getVersionManager())
#set($componentManager = $ComponentAccessor.getProjectComponentManager())
Working with application properties:
#set($appProperties = $ComponentAccessor.getApplicationProperties())
#set($baseUrl = $appProperties.getString("jira.baseurl"))
#set($jiraVersion = $appProperties.getString("jira.version"))
Base URL: $baseUrl
Jira Version: $jiraVersion
Get user information:
#set($userManager = $ComponentAccessor.getUserManager())
#set($user = $userManager.getUserByName("john.doe"))
#if($user)
User: $user.displayName ($user.emailAddress)
Active: $user.isActive()
#end
Access custom components:
## Get any component by class
#set($myService = $ComponentAccessor.getComponent("com.example.MyService"))
#if($myService)
## Use your custom service
#end
## Get OSGi component
#set($osgiService = $ComponentAccessor.getOSGiComponentInstanceOfType("com.example.MyOsgiService"))
Example: Display project and version information
#set($project = $issue.projectObject)
#if($project)
<h4>Project Information</h4>
<p><strong>Project:</strong> $project.name ($project.key)</p>
<p><strong>Lead:</strong> #if($project.lead) $project.lead.displayName #else Not assigned #end</p>
#set($versions = $versionManager.getVersions($project))
#if($versions && $versions.size() > 0)
<h5>Available Versions:</h5>
<ul>
#foreach($version in $versions)
<li>$version.name
#if($version.isReleased())
(Released)
#else
(Unreleased)
#end
</li>
#end
</ul>
#end
#end
Real-World Example Scenarios
Priority-Based Warning Messages
#if($issue.getPriorityObject().name == "High")
<b style="color:red;">Warning: the issue priority is High!</b>
#else
<p>Normal priority. Work as usual.</p>
#end
Priority-Dependent Validation
#if($formIssue.priority.name == "Blocker")
$fieldDisplayConfig.setMessageType("error")
#if(!$formIssue.description || $formIssue.description.isEmpty())
$fieldDisplayConfig.setShowForFieldId("description")
State the reason for the Blocker priority.
#elseif(!$formIssue.assignee)
$fieldDisplayConfig.setShowForFieldId("assignee")
Indicate the responsible employee
#else
$fieldDisplayConfig.setHidden(true)
#end
#elseif($formIssue.priority.name == "High")
$fieldDisplayConfig.setMessageType("warning")
#if($formIssue.description.isEmpty())
$fieldDisplayConfig.setShowForFieldId("description")
State the reason for the High priority.
#else
$fieldDisplayConfig.setHidden(true)
#end
#elseif($formIssue.priority.name == "Medium" || $formIssue.priority.name == "Low")
$fieldDisplayConfig.setHidden(true)
#end
Assignee Information with External Links
#if($issue.assignee)
<p>Assignee: $issue.assignee.displayName</p>
<p>Email: $issue.assignee.emailAddress</p>
#set($slackLink = $issue.assignee.getPropertyValue("slackLink"))
#if($slackLink)
<p>Slack: <a href="$slackLink" target="_blank">$slackLink</a></p>
#else
<p>No Slack link specified.</p>
#end
#else
<p><i>No assignee</i></p>
#end
Issue Type-Specific Messages
#set($typeName = $issue.issueTypeObject.name)
#if($typeName == "Bug")
<p>Important for bugs: ensure reproducible steps are provided.</p>
#elseif($typeName == "Task")
<p>Task: verify deadlines and resources.</p>
#else
<p>Issue type: $typeName. No extra instructions required.</p>
#end
Component-Based Team Assignments
#set($components = $issue.getComponents())
#if($components && $components.size() > 0)
<p>Selected components:</p>
<ul>
#foreach($comp in $components)
<li>
$comp.name
#if($comp.name == "UI/UX")
- The design team is responsible for mockup review
#elseif($comp.name == "Backend")
- The backend team handles microservices
#elseif($comp.name == "Mobile")
- The mobile team checks compatibility with iOS/Android
#end
</li>
#end
</ul>
#else
<p style="color:orange;">No components selected. Please specify at least one!</p>
#end
Due Date Warnings
#set($dueDate = $issue.getDueDate())
#if($dueDate && $dueDate.before($actionDate))
<p>The issue is overdue! Date: $dueDate</p>
#else
<p>The issue is on schedule or no due date is set.</p>
#end
Fix Version Validation
#set($fixVersions = $issue.getFixVersions())
#if($fixVersions && $fixVersions.size() > 0)
<p>Planned for versions:
#foreach($ver in $fixVersions)
<strong>$ver.name</strong><br/>
#end
</p>
#else
<p>No version selected. Please specify the release.</p>
#end
Issue Type Requirements
#set($type = $issue.issueTypeObject.name)
#if($type == "Bug")
<p>Please provide reproducible steps.</p>
#elseif($type == "Story")
<p>Please describe the expected result and acceptance criteria.</p>
#else
<p>No special requirements for type: $type</p>
#end
Budget Threshold Alerts
#set($budgetField = $cfValues.getFromForm("Budget")))
#if($budgetField && $budgetField > 100000)
<p style="color:red;">Warning! Budget exceeds 100,000. Additional check required.</p>
#else
<p>Budget is within acceptable limits.</p>
#end
Component-Specific Reminders
#set($componentList = $issue.getComponents())
#set($hasUIUX = false)
#set($hasBackend = false)
#foreach($comp in $componentList)
#if($comp.name == "UI/UX")
#set($hasUIUX = true)
#elseif($comp.name == "Backend")
#set($hasBackend = true)
#end
#end
#if($hasUIUX)
<p style="color:purple;">Please coordinate mockups with the design team.</p>
#end
#if($hasBackend)
<p style="color:green;">Check compatibility with the microservices architecture.</p>
#end
#if(!$hasUIUX && !$hasBackend)
<p>No specific components selected, proceeding under the standard workflow.</p>
#end
Attachment Type Detection
#set($attachments = $issue.getAttachments())
#if($attachments && $attachments.size() > 0)
Attachments in this issue:
#foreach($attachment in $attachments)
#if($attachment.filename.endsWith(".xlsx"))
- Excel File: <a href="$attachment.url" target="_blank">$attachment.filename</a>
#elseif($attachment.filename.endsWith(".pdf"))
- PDF Document: <a href="$attachment.url" target="_blank">$attachment.filename</a>
#else
- File: <a href="$attachment.url" target="_blank">$attachment.filename</a>
#end
#end
#else
No attachments found.
#end
Additional variables
Apart from the services described above, the following variables are also available in templates:
Context and Transition Variables
$context
- Current context (CREATE, EDIT, VIEW, TRANSITION, etc.)$transitionId
- ID of the current transition (if in transition context)$transitionName
- Name of the current transition (if in transition context)$previousStatusId
- ID of the previous status (from issue history)$previousStatusName
- Name of the previous status (from issue history)$dateOfLastTransition
- Date when the last transition occurred$secAfterLastTransition
- Seconds elapsed since last transition
User and Locale Variables
$currentUser
- Currently logged-in user$locale
- Current user’s locale$language
- Current language code (e.g., “en”, “de”)$country
- Current country code$timeZone
- Current user’s timezone$nowInUserTimeZone
- Current date/time in user’s timezone$currentDateTime
- Current date/time
URLs and Base Information
$baseUrl
- Jira base URL
Show different approval workflows based on user group membership.
#if($userGroupService.isUserInGroup($currentUser, "executives"))
<div style="border: 2px solid gold; padding: 10px; background: #fff8dc;">
<h4>🏆 Executive Approval Process</h4>
<p>As an executive, you can approve high-priority requests immediately.</p>
<p>Your approval supersedes all other requirements.</p>
</div>
#elseif($userGroupService.isUserInGroup($currentUser, "managers"))
<div style="border: 2px solid blue; padding: 10px; background: #e6f3ff;">
<h4>👔 Manager Approval Required</h4>
<p>As a manager, you can approve requests up to $10,000.</p>
<p>For amounts above $10,000, executive approval is required.</p>
</div>
#elseif($userGroupService.isUserInGroup($currentUser, "team-leads"))
<div style="border: 2px solid green; padding: 10px; background: #e6ffe6;">
<h4>🎯 Team Lead Review</h4>
<p>As a team lead, you can approve team resource requests.</p>
<p>Budget requests require manager approval.</p>
</div>
#else
<div style="border: 2px solid orange; padding: 10px; background: #fff4e6;">
<h4>📋 Standard Process</h4>
<p>Your request will go through the standard approval workflow:</p>
<p>Team Lead → Manager → Executive (if required)</p>
</div>
#end
Result (for executive user):
🏆 Executive Approval Process
As an executive, you can approve high-priority requests immediately.
Your approval supersedes all other requirements.
Result (for manager user):
👔 Manager Approval Required
As a manager, you can approve requests up to $10,000.
For amounts above $10,000, executive approval is required.
Result (for team lead user):
🎯 Team Lead Review
As a team lead, you can approve team resource requests.
Budget requests require manager approval.
Result (for regular user):
📋 Standard Process
Your request will go through the standard approval workflow:
Team Lead → Manager → Executive (if required)
Restrict issue creation based on user group and show appropriate guidance.
#if($context == "CREATE")
#if($userGroupService.isUserInGroup($currentUser, "restricted-users"))
$fieldDisplayConfig.setMessageType("error")
<h4>❌ Access Restricted</h4>
<p>Your account has limited permissions for creating issues in this project.</p>
<p>Please contact your project administrator to request access.</p>
<p>Administrator: <strong>admin@company.com</strong></p>
#elseif($userGroupService.isUserInGroup($currentUser, "external-contractors"))
$fieldDisplayConfig.setMessageType("warning")
<h4>⚠️ External Contractor Guidelines</h4>
<p>As an external contractor, please ensure:</p>
<ul>
<li>All issues are clearly described with acceptance criteria</li>
<li>Estimated hours are provided in the "Story Points" field</li>
<li>Contact person is specified in the description</li>
</ul>
#elseif($userGroupService.isUserInGroup($currentUser, "new-employees"))
$fieldDisplayConfig.setMessageType("info")
<h4>👋 Welcome New Team Member!</h4>
<p>As a new employee, here are some tips for issue creation:</p>
<ul>
<li>Use clear, descriptive summaries</li>
<li>Select appropriate issue types (Bug, Task, Story)</li>
<li>Set priority based on business impact</li>
<li>Ask your mentor if you're unsure about any field</li>
</ul>
#end
#end
Result (for restricted user on create screen):
Result (for external contractor on create screen):
Result (for new employee on create screen):
Display team-specific notifications based on assignee’s group membership.
#if($formIssue.assignee)
#set($assignee = $formIssue.assignee)
#if($userGroupService.isUserInGroup($assignee, "mobile-team"))
<div style="background: #e8f5e8; border-left: 4px solid #4caf50; padding: 10px;">
<h4>📱 Mobile Team Assignment</h4>
<p><strong>Assigned to:</strong> $assignee.displayName (Mobile Team)</p>
<p><strong>Important:</strong> Please test on both iOS and Android platforms</p>
<p><strong>Review Checklist:</strong></p>
<ul>
<li>Cross-platform compatibility</li>
<li>Performance on low-end devices</li>
<li>App store compliance</li>
</ul>
</div>
#elseif($userGroupService.isUserInGroup($assignee, "backend-team"))
<div style="background: #e8f4fd; border-left: 4px solid #2196f3; padding: 10px;">
<h4>⚙️ Backend Team Assignment</h4>
<p><strong>Assigned to:</strong> $assignee.displayName (Backend Team)</p>
<p><strong>Important:</strong> Consider microservices architecture</p>
<p><strong>Review Checklist:</strong></p>
<ul>
<li>API documentation</li>
<li>Database migration scripts</li>
<li>Performance and scalability</li>
<li>Security considerations</li>
</ul>
</div>
#elseif($userGroupService.isUserInGroup($assignee, "qa-team"))
<div style="background: #fef7e8; border-left: 4px solid #ff9800; padding: 10px;">
<h4>🧪 QA Team Assignment</h4>
<p><strong>Assigned to:</strong> $assignee.displayName (QA Team)</p>
<p><strong>Testing Focus:</strong></p>
<ul>
<li>Functional testing based on acceptance criteria</li>
<li>Regression testing for related features</li>
<li>Cross-browser/device compatibility</li>
<li>Performance testing if applicable</li>
</ul>
</div>
#else
<div style="background: #f5f5f5; border-left: 4px solid #9e9e9e; padding: 10px;">
<h4>👤 General Assignment</h4>
<p><strong>Assigned to:</strong> $assignee.displayName</p>
<p>Please follow standard development practices and contact your team lead if you need guidance.</p>
</div>
#end
#else
<div style="background: #ffebee; border-left: 4px solid #f44336; padding: 10px;">
<h4>⚠️ No Assignee</h4>
<p>This issue needs to be assigned to a team member before work can begin.</p>
</div>
#end
Result (assignee is mobile team member - John Smith):
📱 Mobile Team Assignment
Assigned to: John Smith (Mobile Team)
Important: Please test on both iOS and Android platforms
Review Checklist:
- Cross-platform compatibility
- Performance on low-end devices
- App store compliance
Result (assignee is backend team member - Sarah Johnson):
⚙️ Backend Team Assignment
Assigned to: Sarah Johnson (Backend Team)
Important: Consider microservices architecture
Review Checklist:
- API documentation
- Database migration scripts
- Performance and scalability
- Security considerations
Result (assignee is QA team member - Mike Wilson):
🧪 QA Team Assignment
Assigned to: Mike Wilson (QA Team)
Testing Focus:
- Functional testing based on acceptance criteria
- Regression testing for related features
- Cross-browser/device compatibility
- Performance testing if applicable
Result (assignee is general user - Jane Doe):
👤 General Assignment
Assigned to: Jane Doe
Please follow standard development practices and contact your team lead if you need guidance.
Result (no assignee):
⚠️ No Assignee
This issue needs to be assigned to a team member before work can begin.