Agile advocate in the room is screaming! How can you have 2 fix versions for a single ticket? How? How? How?
Several scenes (and days) later, Scene N:
Q: We need to restrict the fixVersion(s) field to have only one version. And we need it before the next meeting. Can you?
A: Yup, you are not the first one to ask. Let's do it!
Well, that was the plot! The fixVersion(s) field in JIRA allows multiple versions and this was one of the cases were the customer wanted to restrict the field to have only one version selected.
Doing this was pretty easy in the earlier versions of JIRA where it was a simple multiselect field. All you had to do was to find the appropriate velocity template that is used to render the field and make it a single select instead of multi select. The template was verions-edit.vm under /atlassian-jira/WEB-INF/classes/templates/jira/issue/field and that was it!
In JIRA 4.4.3, things are a bit more tricky. The fixVersion field is rendered using the same template but there is no multi-select field on the screen now. Well, there is one but it is hidden! Instead, the visible field is an Ajax field that populates the options as the user starts typing matching letters. And as the user selects one of the options, the multi select field takes that value.
You can select more value, ofcourse! And that is what we wanted to restrict.
The easy choice and perhaps the best (we will see why) is to do a Javascript validation on the submission of the form. Adding Javascript on a field is pretty easy in JIRA. All you need to do is to edit the field's description in the appropriate field configuration and add the Javascript there. This has an added advantage that the Javascript needs to be added only in the field configurations that you want and hence you can limit the restrictions for selected projects, all by simple configuration! That is why I said it is probably the best solution.
And Javascript validation can be done easily using AJS:
- Identify the fixVersion field
- Find out the nearest form. This is important because we don't want to hard code the create or edit form ids. Instead, this should work wherever the fixVersion field is added!
- Add the submit handler for the form and check the number of fixVersions selected
- Alert the user if the number of fixVersions is more than one!
Here is what I did:
<script >
var formId = AJS.$("#fixVersions").closest('form').attr('id');
AJS.$('#'+formId).submit(function() {
var count = AJS.$("#fixVersions :selected").length;
if (count > 1){
alert('You can select only one Fix Version for an issue');
return false;
} else {
return true;
}
});
</script>
And this is what happened when they selected multiple fix versions:
But, ah the buts, someone asked the question I was trying to avoid. Isn't this a reactive solution? Throwing an error when they submit the form, after they select multiple versions? Can we do something proactive? i.e. just prevent people from selecting it?
Time to hack the AJS scripts that does the population of options.
Now, if you trace back from the class used in aui params in the versions-edit.vm, aui-field-versionspicker, you will find that initVersionPickers.js file uses it to create the selector picker. And the function used is AJS.MultiSelect. The AJS.MultiSelect function is declared in /atlassian-jira/includes/ajs/select/MultiSelect.js file. And that, is the file where I put my hack!
There might be different places where you can put the hack in but I found it appropriate to place it along side the duplicate check. There is a method _isItemPresent in the MultiSelect.js file which checks whether the option selected is already present or not! I have added few simple lines alongside it to check whether the list has atleast one version selected or not. And if it has, we are going to just alert the user and treat it just like the duplicate scenario. Here is the modified method:
_isItemPresent: function (descriptor) {
//For fixVersions, return false if more than one version is selected!
var elementId = this.options.element.attr('id');
if (elementId == 'fixVersions' && this.lozengeGroup.items.length > 0){
alert('You can select only one Fix Version for an issue');
return true;
}
// Normal duplicate check
var duplicate = false;
var value = descriptor.value();
AJS.$.each(this.lozengeGroup.items, function () {
if (this.value === value) {
duplicate = true;
return false; // bail
}
});
return duplicate;
},
And ofcourse, you should check if the field is fixVersions or not because the MutliSelect function is used by other fields like Affected Versions!
And that's it! When you try to select more than one version, user gets the alert then and there. The same alert, but well in advance!
Remember, the change has to be done in the minnified version of the javascript, MultiSelect-min.js as well. And yes, once this is done, it is applied system-wide. i.e. for all the projects. If you need this only for selected projects or you want to remove the checking for selected projects, add extra bits to check for projects. I would try to avoid that and use the first solution in that case.
PS: Don't forget to do this change for every upgrades of JIRA!
PS(2): Like this trick? Have a look at the book to see more!
Edit: May 25, 2012
What if you are adopting the first method and you want to clear out all the fixVersions after the alert is shown? The following worked in response to Srinivas's query in the comments:
<script >
var formId = AJS.$("#fixVersions").closest('form').attr('id');
AJS.$('#'+formId).submit(function() {
var count = AJS.$("#fixVersions :selected").length;
if (count > 1){
alert('You can select only one Fix Version for an issue');
AJS.$("#fixVersions-multi-select").find('em').click();
return false;
} else {
return true;
}
});
</script>
Here the line in bold clears the selected options by triggering a 'click' on the small close icon! - Tested only on 4.4.3.