Discussion:
Adding a User Picker field to a plugin
forums-YrDk4eUcT3KaMJb+
2010-01-08 16:55:32 UTC
Permalink
Hi all,

I'm developing a plugin, and would like to leverage the User Picker field that exists in JIRA.
The use case is that in my plugin I would like to have an assignee field of type User Picker. As a user types into this field, I would like to have the ajax-like suggestions for this assignee field.

Can anyone suggest how I can do this? Thank you!
--
Post by david.lai - online at:
http://forums.atlassian.com/thread.jspa?forumID=100&threadID=40097
forums-YrDk4eUcT3KaMJb+
2010-01-08 22:28:12 UTC
Permalink
Hi,

Have had this problem too.
This could be:
* timeout. Please type something you know is there and wait, wait, for a long. If it's development jira with big userbase, then you could have to wait for 5-15 seconds :))) It's because SQL goes through all the users (SQL like) and nothing is cached yet.
* persmissions. Check you user have all the permission required (see the code for userpicker macro, it does some permission check.. Search for macros.vm, macro.vm or something like this in the JIRA WEB-INF/ folder).

Hopefully it helps,

--Wbr, Leonids M.
--
Post by leonardinius - online at:
http://forums.atlassian.com/thread.jspa?forumID=100&threadID=40097
forums-YrDk4eUcT3KaMJb+
2010-01-09 00:00:53 UTC
Permalink
Thanks for your note Leonids.

Unfortunately, I don't think my problem is a timing issue. Rather, it is my lack of understanding of how to leverage and piece together the necessary parts in the JIRA framework. :(
--
Post by david.lai - online at:
http://forums.atlassian.com/thread.jspa?forumID=100&threadID=40097
forums-YrDk4eUcT3KaMJb+
2010-01-09 00:10:21 UTC
Permalink
Mmm,

Could you provide more information on background than:
* Is this an action template you call macros from?
* Does the user have all the required permissions? (I mean the user performing the action)
* Does Firebug show any javascript/whatever errors?

Obviously see the

{code}
#if ($action.isHasPermission("pickusers"))
{code}

code snippet from macros.vm. The dirty and quick workaround is to override isHasPermission in you action to see it returns indeed true in your case.

Hopefully it helps,

--wbr, Leonids M.
--
Post by leonardinius - online at:
http://forums.atlassian.com/thread.jspa?forumID=100&threadID=40097
forums-YrDk4eUcT3KaMJb+
2010-01-12 22:42:44 UTC
Permalink
*Thank you everyone for chiming in to help.*

Here is more background information of the plugin I am writing. The plugin is a web-item plugin in JIRA version 4.

=== *atlassian-plugin.xml* ===
I have added the following code segment into atlassian-plugin.xml. As a result, I successfully added a new link in the top navigation bar of JIRA.
(ie: Dashboards | Projects | Issues | Administration | *NewAction* )
{code}<web-item key="new_action_link" name="New Action Link" section="system.top.navigation.bar" weight="100">
<label key="item.newactionlink.label" />
<link linkId="groovy_runner">/secure/NewAction!default.jspa</link>
<condition class="com.atlassian.jira.plugin.webfragment.conditions.UserLoggedInCondition" />
</web-item>
{code}
In addition to the above fragment, I also have the following fragment in atlassian-plugin.xml, which hooks a webwork action to my *New Action* link in the top navigation bar.
{code}<webwork1 key="new_action" name="New Action">
<actions>
<action name="com.ams.jira.plugins.NewAction" alias="NewAction">
<view name="input">/templates/new_action.vm</view>
<view name="error">/templates/new_action.vm</view>
<view name="success">/templates/new_action.vm</view>
</action>
</actions>
</webwork1>
{code}

=== *new_action.vm* ===
{code}<html>
<head>
<title>New Action</title>
<meta name="decorator" content="general">

<script type="text/javascript">
var rowCount = 0;
var fieldCount = 0;

function moreFields() {
var newRow = document.getElementById('issueRows').insertRow(2);
newRow.id = rowCount++;

var newCell = newRow.insertCell(0);
newCell.innerHTML = getCellHTML();

attachEventHandlers();
}

function deleteElement(id){
var el = document.getElementById(id);
el.parentNode.removeChild(el);
}

function getUserPickerHTML() {
var fieldName = "assignee" + fieldCount++;

var userPickerHTML = "<fieldset class='hidden user-picker-params' rel='" + fieldName + "'> \
<input id='formName' type='hidden' value='jiraform'/> \
<input id='fieldName' type='hidden' value='" + fieldName + "'/> \
<input id='multiSelect' type='hidden' value='false'/> \
<input id='userPickerEnabled' type='hidden' value='true'/> \
</fieldset> \
<div id='" + fieldName + "_container' class='ajax_autocomplete'> \
<input id='" + fieldName + "' class='userpickerfield' type='text' style='width: 30%;' value='' name='" + fieldName + "'/> \
<a class='popup-trigger' href=''> \
<img width='16' hspace='0' height='16' border='0' align='absmiddle' src='/images/icons/filter_public.gif' name='assigneeImage' title='Select a user'/> \
</a> \
<div id='" + fieldName + "_results' class='ajax_results'/> \
<font size='1'>Start typing to get a list of possible matches.</font> \
</div>";

return userPickerHTML;
}

function attachEventHandlers() {
AJS.$(".user-picker-params").each(function(){
var params = {};
AJS.$(this).find("input").each(function(){
var $this = AJS.$(this);
params[$this.attr("id")] = $this.val();
});

AJS.$("#" + params.fieldName + "_container a.popup-trigger").click(function(e){
if (!params.formName)
{
params.formName = AJS.$(":input[name='" + params.fieldName +"']").parents("form").attr("name");
}
var url = contextPath;

if (params.actionToOpen){
url = url + params.actionToOpen;
}else {
url = url + '/secure/popups/UserPickerBrowser.jspa';
};
url += '?formName=' + params.formName + '&';
url += 'multiSelect=' + params.multiSelect + '&';
url += 'element=' + params.fieldName;

var vWinUsers = window.open(url, 'UserPicker', 'status=yes,resizable=yes,top=100,left=200,width=580,height=750,scrollbars=yes');
vWinUsers.opener = self;
vWinUsers.focus();
e.preventDefault();
});

if (params.userPickerEnabled === "true" ){
jira.widget.autocomplete.Users({
fieldID: params.fieldName,
delimChar: params.multiSelect === "false"? undefined : ",",
ajaxData: {
fieldName: params.fieldName
}
});
}
});
}
</script>

$!webResourceManager.requireResource("jira.webresources:autocomplete")
</head>
<body>

<table cellspacing="0" cellpadding="10" border="0" width="100%"><tr><td>
<form action="NewAction!default.jspa" method="post" name="jiraform" onsubmit="if (this.submitted) return false; this.submitted = true; return true" >
<table class="jiraform maxWidth" id="issueRows">
<tr>
<td colspan="2" class="jiraformheader"><h3 class="formtitle">New Action</h3></td>
</tr>
<tr>
<td colspan="2" class="jiraformheader">
<p>This tool is for New Action. <a href="javascript:;" onclick="moreFields();">Add Row</a></p>
</td>
</tr>

<tr id="pivot">
<td colspan="2" class="fullyCentered jiraformfooter" >
<input type="submit" name="copyProject" value="Create" accesskey="S" title="Press Ctrl+S to submit form" class="spaced"/>
</td>
</tr>
</table>
</form>
<script language="javascript">
window.onload = moreFields;
try { document.jiraform.elements[0].focus(); } catch (e) {}
</script>
</td></tr></table>
</body>
</html>
{code}

The original problem I had faced, was that I could not get the AJAX suggestions to work for the user-picker JIRA field that I have included. What I have failed to explain, is that I was adding in the user-picker field with javascript. That is, the page starts without any fields, and then the user-picker field is dynamically populated using javascript. In contrast, if I had rewritten the velocity template with a static user-picker field (no dynamic population with javascript), then the AJAX suggestions for the user-picker field works.

+The update to my problem statement, is that I faced great difficulty getting JIRA's user-picker field's AJAX suggestion to work, *when the user-picker fields have been populated dynamically with javascript*.+

Fortunately, I have figured out a solution. The code above isn't elegant by any means, it is a prototype I have used to resolve my problem. Please note that my background in javascript is less than a week's worth. Thus, any comments or suggestions with regards to re-factoring are greatly appreciated.

*_Key Observation_*: After much tracing and reverse engineering attempts in trying to figure out how JIRA's user-picker field works, I realized that no event handlers (ie: keyboard key-up event, mouse click event, etc...) are mapped to the new dynamically populate user-picker fields. After spending some time with *autocomplete.js*, I have identified the code segment containing the logic for assigning the event handlers to user-picker fields. I have copied the mentioned code segment from *autocomplete.js* into the *attachEventHandlers()* javascript function. I would much rather have called an existing function that exist in *autocomplete.js*, however, I believe the code segment I have pulled out is an inline javascript function? Thus, there I am under the assumption there is no function nam
e I can readily use within *autocomplete.js* to invoke this logic. As such, I have simply copied the logic into *attachEventHandlers()*, and is calling *attachEventHandlers()* each time a ne
w user-picker field is dynamically added to ensure proper mapping of event handlers are set. This in result, solved the problem.

Thank you to everyone who has pitched in comments and suggestions, and I hope this post will help someone in the community in attempt to achieve similar goals.

-David Lai
--
Post by david.lai - online at:
http://forums.atlassian.com/thread.jspa?forumID=100&threadID=40097
forums-YrDk4eUcT3KaMJb+
2010-01-09 04:26:56 UTC
Permalink
This was working for us with Jira 3.13 but is no longer working with Jira 4.0.1.
--
Post by brianatlawson - online at:
http://forums.atlassian.com/thread.jspa?forumID=100&threadID=40097
forums-YrDk4eUcT3KaMJb+
2010-01-12 23:10:32 UTC
Permalink
Additional note: I originally wanted to leverage the userPicker macro below:
{code}#userPicker ($action "jiraform" "$someFieldName" "" false $null " width: 30%;" true 10)
{code}
However, I could not figure out a nice way of using this macro with javascript to dynamically populate the user-picker fields. When the macro is coded into a velocity template, it gets interpolated into HTML according to the macro definition. I need to pass in unique field name into the macro, so I have attempted to pass in $someFieldName, which in turn will invoke a *getSomeFieldName()* method in a class within my plugin. The problem is that $someFieldName gets interpolated at page load (at beginning), and becomes static HTML content. Therefore, I cannot reuse the HTML generated by the macro for future user-picker field population, because I can't pass in unique field names (from calling *getSomeFieldName()* ) to the macro after the initial interpolation at page load.

Any suggestions? I can further clarify the problem if required, it is not an easy problem to express. Thanks in advance.
--
Post by david.lai - online at:
http://forums.atlassian.com/thread.jspa?forumID=100&threadID=40097
forums-YrDk4eUcT3KaMJb+
2010-01-12 23:22:07 UTC
Permalink
Post by forums-YrDk4eUcT3KaMJb+
Therefore, I cannot reuse the HTML generated by the macro for future user-picker field population, because I can't pass in unique field names (from calling *getSomeFieldName()* ) to the macro after the initial interpolation at page load.
Why? I see you already use AJS.$. You could use AJS.$ or jQuery directly to obtain any html element you want.
So, you could find the html fragment generated, modify it, copy it to another place...

Please collaborate on details: what exactly you were going to do with this +generated (static)+ html fragment?
--
Post by leonardinius - online at:
http://forums.atlassian.com/thread.jspa?forumID=100&threadID=40097
forums-YrDk4eUcT3KaMJb+
2010-01-13 00:13:02 UTC
Permalink
Thanks for the reply Leonids.

I am actually not familiar at all with AJS or jQuery. The logic within *attachEventHandlers()* was copied straight from JIRA's *autocomplete.js* Thank you for reminding me to take advantage of AJS and jQuery. I will have to learn and look into that.

Ideally, I wouldn't have to work with the static html fragment generated by the userPicker macro. I simply want to have a way to invoke a call, that will return me the html fragment for JIRA's user-picker field. I want to be able to invoke this call on demand to dynamically populate my page with user-picker fields after the initial page load.

The userPicker macro was the the first and closest thing that I came across. Unfortunately, it is not a method call, but it is a macro. As such, I cannot avoid interpolation to static html at page load time. So, I have written *getUserPickerHTML()* to reproduce the html content that the userPicker macro gives, but I would much rather +reuse+ than +reproduce+.

Please let me know if I have better communicated my desired use case.
--
Post by david.lai - online at:
http://forums.atlassian.com/thread.jspa?forumID=100&threadID=40097
Leonid M.
2010-01-13 08:05:50 UTC
Permalink
Hi,

ok, I see. In my view you have several options:
* Copy HTML and use it (as in your previous post)
* Use macros from your template and adjust / copy it with AJS.$ and/or
jQuery
* Render macros from your code from sosme predefined velocity template
{code}
VelocityManager velocityManager;
Map velocityParams;
velocityManager = ManagerFactory.getVelocityManager();
velocityParams = createVelocityParams(startingParams);

return velocityManager.getEncodedBody(resource.getLocation(), "",
ManagerFactory.getApplicationProperties().getEncoding(), velocityParams);
{code}
and then modify it as you need via substring/regexp/whatever.

Hopefully it helps,
--
Best regards,
Leonids Maslovs

Loading...