#4549 closed enhancement (fixed)
Enable HoursInPlaceEditor functionality for reports
Reported by: | Jason Winnebeck | Owned by: | Joachim Hoessler |
---|---|---|---|
Priority: | normal | Component: | EstimationToolsPlugin |
Severity: | normal | Keywords: | |
Cc: | Trac Release: | 0.11 |
Description
Enable the HoursInPlaceEditor to work with custom reports.
The request handler only injects the javascript into the custom query pages. This is probably because the script, as is, doesn't work for report pages, for three reasons:
- The column for the ticket numbers has class "ticket" whereas in query it has class "id". The class is "ticket" in reports regardless of whether or not the report's columnname is "id" or "ticket"
- The HTML generated for the ticket column in the report module has a lot of extra whitespace added. The script assumes that the column's text is like "#123", with no spaces, and that all of the characters in the string after the first form a number.
- The path to the xmlrpc is not correct when in a report page.
I fixed these three issues with the patch below. A disclaimer: I program Java, C#, C++, and a little Python out of necessity for Trac but I've never done JavaScript or JQuery, so there's probably better ways to do it. Also, a lot of whitespace changed so the changes below look like more than they are. Only the top portion of the script changed.
-
estimationtools/hoursinplaceeditor.py
28 28 return handler 29 29 30 30 def post_process_request(self, req, template, data, content_type): 31 if (req.path_info.startswith('/query') 31 if (req.path_info.startswith('/query') or req.path_info.startswith('/report') 32 32 and req.perm.has_permission('TICKET_MODIFY') 33 33 and req.perm.has_permission('XML_RPC')): 34 34 # add_script(req, 'estimationtools/jquery-1.2.3.min.js') -
estimationtools/templates/edithours.html
2 2 xmlns:xi="http://www.w3.org/2001/XInclude" 3 3 xmlns:py="http://genshi.edgewall.org/" py:strip=""> 4 4 $(document).ready(function() { 5 ids = $('td.id').each(function(i) { 6 var id = $(this).text().substr(1); 7 var estimationField = '$data.field'; 5 var reportMethod = $('td.ticket'); //used in Trac reports 6 var queryMethod = $('td.id'); //used in custom queries 8 7 9 var estimationCell = $('td.' + estimationField).eq(i); 8 var xmlrpcPath = null; 9 var elements = null; 10 10 11 estimationCell.editable(function(value, settings) { 12 var currentElement = this; 13 $.ajax({ 14 type: 'POST', 15 url: 'xmlrpc', 16 data: '<methodCall><methodName>ticket.update</methodName>' + 17 '<params><param><value><int>' + id + '</int></value></param>' + 18 '<param><value><string></string></value></param>' + 19 '<param><value><struct><member><name>' + estimationField + '</name>' + 20 '<value><string>' + value + '</string></value></member></struct></value></param>' + 21 '</params></methodCall>', 22 contentType: 'text/xml', 23 success: function(){ 24 $(currentElement).text(value); 11 if ( reportMethod.length != 0 ) { 12 elements = reportMethod; 13 xmlrpcPath = '../xmlrpc'; 14 } else if ( queryMethod.length != 0 ) { 15 elements = queryMethod; 16 xmlrpcPath = 'xmlrpc'; 17 } 18 if ( elements != null ) { 19 ids = elements.each(function(i) { 20 var id = jQuery.trim($(this).text()).substr(1); 21 var estimationField = '$data.field'; 22 23 var estimationCell = $('td.' + estimationField).eq(i); 24 25 estimationCell.editable(function(value, settings) { 26 var currentElement = this; 27 $.ajax({ 28 type: 'POST', 29 url: xmlrpcPath, 30 data: '<methodCall><methodName>ticket.update</methodName>' + 31 '<params><param><value><int>' + id + '</int></value></param>' + 32 '<param><value><string></string></value></param>' + 33 '<param><value><struct><member><name>' + estimationField + '</name>' + 34 '<value><string>' + value + '</string></value></member></struct></value></param>' + 35 '</params></methodCall>', 36 contentType: 'text/xml', 37 success: function(){ 38 $(currentElement).text(value); 25 39 } 26 40 }); 27 41 return('Saving...'); 28 42 }, { 29 30 31 32 33 34 35 43 data : jQuery.trim(estimationCell.text()), 44 tooltip : 'Click to edit...', 45 placeholder: '', 46 onblur : 'submit', 47 select : 'true', 48 style : 'inherit', 49 width : 60 36 50 }); 37 51 }); 52 } 38 53 }); 39 </script> 40 41 No newline at end of file 54 </script> 55
What I did is check to see whether we have "td.ticket" (for reports) or "td.id" fields (for query), then modify the behavior appropriately. I trimmed the ticket string so that the old "substr(1)" method will work regardless. If I knew how to do regular expressions in JavaScript, I would have done it that way. Also, I don't know if the closures that I used (using var xmlrpcPath in the calling code and the anonymous function) is safe/proper style.
I'm currently using this script in my 0.11.2 Trac, because we need to work with a report that is not possible with the "custom query" system.
This ticket is similar to a previously closed ticket I found, #4003.
Attachments (0)
Change History (4)
comment:1 Changed 16 years ago by
comment:2 Changed 16 years ago by
Sorry for another ticket spam, but I just wanted to note that in the report's SQL, you must name the value
column joined from the ticket_custom
table the same name as the estimation_field
parameter in the configuration.
For example, my field is called hours_remaining
, and I have used the combination of this plugin, MasterTicketsPlugin, and a new ticket type "PBI" to simulate a PBI/SBI list as used in the Scrum agile development process. This list can be used for standup meetings.
SELECT p.value AS __color__, 'PBI ' || parent.id || ': ' || parent.summary || ' (' || (SELECT SUM(hours.value) FROM mastertickets mt LEFT JOIN ticket_custom hours ON mt.source = hours.ticket AND hours.name = 'hours_remaining' WHERE mt.dest=parent.id) || ' hours)' AS __group__, t.id AS ticket, t.summary as summary, t.component as component, t.type AS type, t.owner AS owner, t.status AS status, t.time AS created, t.changetime AS _changetime, t.description AS _description, t.reporter AS _reporter, t.priority AS _priority, hours.value AS hours_remaining FROM ticket t INNER JOIN enum p ON t.priority = p.name AND p.type = 'priority' INNER JOIN mastertickets parentId ON t.id = parentId.source INNER JOIN ticket parent ON parentId.dest = parent.id LEFT JOIN ticket_custom hours ON t.id = hours.ticket AND hours.name = 'hours_remaining' WHERE parent.status <> 'closed' AND t.type <> 'PBI' ORDER BY t.milestone, p.value, t.time
The important parts are the LEFT JOIN ticket_custom
... and the hours.value AS hours_remaining
. If you don't do that, the script won't work and will not give any indication that anything went wrong, or that it even tried to do something.
comment:3 Changed 16 years ago by
Resolution: | → fixed |
---|---|
Status: | new → closed |
Another note on this -- the script is processed using Genshi but however the processor is used is enforcing that the resulting document is a properly formed XML fragment. That means I couldn't use code like
x > 0
. Instead I had to sayx != 0
. I'm not sure how to work Genshi to resolve this issue. I think the "manual" XML-RPC AJAX call in the script works because Genshi is interpreting the code as an arbitrary XML fragment with text data between each of the elements, and it just "happens to work."