Subpages:
Permissions required: "Browse users and groups" -> global permission. |
According GDPR, this app does not store any personal data by itself. All user entered data are stored within Jira properties within your own Jira instance hosted by Atlassian: no data will be stored outside your Jira instance at all!
Please create new fields of type "Group Sign-Off" as many as you need, like "Steering Committee", "Product Board" etc.
You have to determine, who is allowed to make a decision. Using the feature of setting a default value of a customfield, you can define it once and it will be taken over while creating a new issue automatically.
A definition of a decision is divided into two parts and stored as value within the related customfield:
The list starts at the first character, first line with the login name of the responsible user: one user per line. Each line has to be terminated by a line-break. If the login name is unknown, it will not be displayed on the screen. The list of users end with a blank line for a better overview.
The rule for the decision starts with "sign-off=" followed by a boolean expression. Internal validation of the rule will be done by replacing all login names by their related decisions (sign-off = true and decline = false, pending = nothing). As soon as this rule results in a final TRUE or FALSE, it triggers the configured listener (see below). Having no individual decisions at the beginning or necessary pending ones, the result cannot be determined and nothing happens. The boolean expression can contain brackets "(" and ")" as well as "AND" and "OR" to describe the relationship of individual decisions. If the IT-boss or her/his representative can decide, one of both sign-offs is enough, and the business product owner has to sign-off as well the rule would be like: (boss OR representative) AND productOwner.
If an automatic conversion from a user name or email into an account id is not possible, which depends on various scenarios and cannot be simply answered, you have to use the account id of deciders instead. Generally, that should not be necessary but in case you need it for uniqueness, you can find the accountId of a user as described below.
The easiest way to find your account id is to click on your icon on the sidebar (left/most down icon with your avatar image) and then on the "Profile" link. Then, have a look at the URL within your browser: your accountId is the string after the last "/" marked bold in the sample below:
Sample: https://******.atlassian.net/people/557058:7b5dfd59-30f7-4f0e-864d-34fb8ba6e452
Such accountId has to be adjusted by replacing the colon (":") by double underscores ("__") and all minus ("-") by a single underscore ("_").
fpolscheit representative sign-off=(fpolscheit OR representative) |
If you want to put additional information to the user name, you can add this as a comment straight after the user name and enclosed by /* and */:
fpolscheit /* Manager */ representative /* Alternative Voting */ sign-off=(fpolscheit /* Manager */ OR representative /* Alternative Voting */) |
By clicking on "Update", each user name will be replaced by it's related account id before storage within a Jira property due to GDPR. If the automatic migration to an account id is not possible, a WARNING hint will be written at the end of the rule as comment and a related warning sign will be displayed on the right side of the field name within the overview list of all group sign-off fields. Such scenario occurs, if for example a specified user name is not unique (API request /rest/api/3/user/search?query=username results in multiple account ids).
Use comments to display them as additional infos:
Instead of explicitly declare a list of users and a sign-off rule using boolean algebra, you can specify a dynamic rule, which extracts the users out of referred other fields like multi-user pickers. A dynamic rule MUST start with a first line just containing "// conditional rule". Then, users and a rule has to be defined as described below:
Within a dynamic rule, the reserved words in blue within the following complex example are mandatory!
Within a condition, you can use all methods provided by JIRA's issue API. The helper object provides the following methods:
If you are using getUsersByCustomfield() etc. then the name of the referred element (e.g. field) will be displayed in gray in addition to the user name. So, a user knows her/his context if displayed multiple times, for example by belonging to various internal roles.
*) According to Atlassian's restrictions, access to users per project role or group can only be done by users with admin permissions! Therefore, each time you modify the members of a group or project role, you have to click on the "sync" button as admin in order to provide this infos to the Group Sign-Off app in the context of all logged-in users.
// conditional rule users =""+helper.getUsersByCustomfield(issue,"Board Members",","); |
Please use the exact syntax above for dynamically specifying the list of user(s) based on the referred custom field. Users must be a comma separated list, that why the operand is a comma. The rule must be a sequence of users, concatinated by a boolean operand (AND or OR). By the first voting, the content of the customfield, referenced by name as parameter, will be taken over and the members will become a fix list.
Instead of using the custom field's name, you can also use it's ID: please put that number into the quotes like "10023" instead of "Board Members".
// conditional rule if ( helper.contains(issue.components, "Component A") ) { users = "fpolscheit, admin"; rule = "fpolscheit OR admin"; } else if ( helper.contains(issue.components, "Component B") && (issue.priority.name == "Major")) { users = "admin, tester"; rule = "admin AND tester"; } else { users = "admin"; rule = "admin"; } |
If an automatic conversion from a user name or email into an account id is not possible, which depends on various scenarios and cannot be simply answered, you have to use the account id of deciders instead. Generally, that should not be necessary but in case you need it for uniqueness, you can find the accountId of a user as described below.
The easiest way to find your account id is to click on your icon on the sidebar (left/most down icon with your avatar image) and then on the "Profile" link. Then, have a look at the URL within your browser: your accountId is the string after the last "/" marked bold in the sample below:
Sample: https://******.atlassian.net/people/557058:7b5dfd59-30f7-4f0e-864d-34fb8ba6e452
Such accountId has to be adjusted by replacing the colon (":") by double underscores ("__") and all minus ("-") by a single underscore ("_") if being used as a hardcoded reference within your dynamic rule.
You can also access all fields of an issue, like within the following sample of extracting the issue's reporter, and use fields' information for approval:
// conditional rule |
You can use "helper.log(issue)" to display all available fields and the elements' structure within your browser's console.
If you are using multiple custom fields to retrieve all users, who have to decide all together, you have to concatenate the custom fields content and use a logical operator within the dynamic rule. The sample below illustrates proper usage.
// conditional rule users =""+helper.getUsersByCustomfield(issue,"Board Members",",") + helper.concat(helper.getUsersByCustomfield(issue,"Manager",",") , ","); |
Another sample of displaying the Group Sign-Off field in a certain status, only:
// conditional rule users =""; |
Another sample of displaying the Group Sign-Off field depending on a certain value of a multi-select custom field:
// conditional rule users =""; |
Behave differently if the issue's reporter is a member of a specified project role:
// conditional rule if (helper.removeContexts(helper.getUsersByProjectRole(issue,"Administrators",",")).indexOf(issue.reporter.accountId.replace(/:/g,"__").replace(/-/g,"_")) > -1) { // issue has been created by a member of project role 'Administrators' // no decisions are necessary: ignore group sign-Off (do nothing) users = ""; rule = ""; } else { users =""+helper.getUsersByCustomfield(issue,"Approvers",","); rule =""+helper.getUsersByCustomfield(issue,"Approvers","OR"); } |
with param-1: Boolean value TRUE or FALSE, whereas true indicates sign-off and false indicates decline
param-2: threshold, resp. minimum amount of decisions as specified within param1 to return the value of param1, otherwise this function will return null
param-3, ..., param-n: comma-separated list of deciders
fpolscheit sign-off=check(true, 2, fpolscheit,representative,anotherPerson) OR check(false, 1, fpolscheit,representative,anotherPerson) |
If at least one decider declines, the sign-off will be "declined" or if at least two deciders have voted for sign-off, the result will be "signed-off".
Based on 3 possible results of function check(null, true, false), the sequence of both function calls combined with OR is important: if you change both, it will not work properly! Based on boolean logic, false OR null results in null, whereas null OR false results in false which is what you would like to get. So, the check for declined votes via check(false,...) has to be the second part after the OR-connector.
A more complex sample combines the check-function with other dynamic helper-functions retrieving sets of deciders: at least 2 deciders out of the project roles "Developers" and "Administrators" have to sign-off, so that this rule switches into the state "signed-off". But if 1 decider declines, it results into "declined", immediately.
// conditional rule |
Expanation: using a dynamic rule, that rule must get a string in quotes like rule = "check(true, 2, x, y, z) OR check(false, 1, x, y, z)", whereas x, y, z is build by helper-functions like a concat of getUsersByProjectRole().
Additional options can be added at the end of the definition of any rule like shown below.
optionDecisionWithEffectOnly : by default, all deciders will see the buttons for declining or signing off. If you want to reduce that just for deciders, whose decision has got an immediate effect on the final result then use this option.
In that case, a decline as well as a sign-off decision for all deciders without any already taken decisions will be simulated. This depends heavily on the complexity of your dynamic rule! So, it might occur that you have to contact me via email at frank@polscheit.de to assist or adapt in specific cases.
As admin, please ensure that all your deciders are members of the default Jira group named "users": it's not enough to be a member of "jira-users", only! Best practice: assign all your users to the group "users".
Then, you can dis-/enable email notifications within the app's configuration main panel by clicking on the related checkbox as displayed on the sample screen copy on the right side.
Alternatively, each decider can create a Jira filter based on JQL, saved that under a suitable name and activate filter subscription for this named filter, which is a standard feature of Jira: you can specify the frequency and at what day and time you will receive an email with a summary of all outstanding issues to be decided etc. That is very helpful, especially if you are spammed with too many emails. Also see: Usage (Jira Cloud)#Notificationsfordeciders(searchissuesforpendingdecisionsviaJQL)