Modify

Opened 5 years ago

Closed 5 years ago

Last modified 5 years ago

#4549 closed enhancement (fixed)

Enable HoursInPlaceEditor functionality for reports

Reported by: JasonWinnebeck Owned by: 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:

  1. 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"
  2. 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.
  3. 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

     
    2828        return handler 
    2929 
    3030    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') 
    3232            and req.perm.has_permission('TICKET_MODIFY') 
    3333            and req.perm.has_permission('XML_RPC')): 
    3434            # add_script(req, 'estimationtools/jquery-1.2.3.min.js') 
  • estimationtools/templates/edithours.html

     
    22      xmlns:xi="http://www.w3.org/2001/XInclude" 
    33      xmlns:py="http://genshi.edgewall.org/" py:strip=""> 
    44$(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 
    87 
    9           var estimationCell = $('td.' + estimationField).eq(i); 
     8    var xmlrpcPath = null; 
     9    var elements   = null; 
    1010 
    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); 
    2539           } 
    2640         }); 
    2741         return('Saving...'); 
    2842       }, { 
    29                 data : jQuery.trim(estimationCell.text()), 
    30                 tooltip   : 'Click to edit...', 
    31                 placeholder: '', 
    32                 onblur : 'submit', 
    33                 select : 'true', 
    34                 style : 'inherit', 
    35                 width     : 60 
     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 
    3650       }); 
    3751    }); 
     52    } 
    3853 }); 
    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 5 years ago by JasonWinnebeck

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 say x != 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."

comment:2 Changed 5 years ago by JasonWinnebeck

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 5 years ago by hoessler

  • Resolution set to fixed
  • Status changed from new to closed

(In [5360]) Applied patch by Jason Winnebeck to enable HoursInPlaceEditor functionality in reports. This closes #4549

comment:4 Changed 5 years ago by hoessler

Thanks for the patch!

Add Comment

Modify Ticket

Action
as closed .
as The resolution will be set. Next status will be 'closed'.
to The owner will be changed from hoessler. Next status will be 'closed'.
The resolution will be deleted. Next status will be 'reopened'.
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.