Opened 8 years ago

Closed 8 years ago

Last modified 8 years ago

#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


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/

    2828        return handler
    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=""
    33      xmlns:py="" py:strip="">
    44$(document).ready(function() {
    5     ids = $('').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 = $('');      //used in custom queries
    9           var estimationCell = $('td.' + estimationField).eq(i);
     8    var xmlrpcPath = null;
     9    var elements   = null;
    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';
     23        var estimationCell = $('td.' + estimationField).eq(i);
     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>
    41  No newline at end of file

What I did is check to see whether we have "td.ticket" (for reports) or "" 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 8 years ago by Jason Winnebeck

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 8 years ago by Jason Winnebeck

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.summary || ' (' ||
         (SELECT SUM(hours.value) FROM mastertickets mt
          LEFT JOIN ticket_custom hours ON mt.source = hours.ticket AND
                                  = 'hours_remaining'
          WHERE ||
         ' hours)' AS __group__, 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 = AND p.type = 'priority'
INNER JOIN mastertickets parentId ON = parentId.source
INNER JOIN ticket parent ON parentId.dest =
LEFT JOIN ticket_custom hours ON = hours.ticket AND = '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 8 years ago by Joachim Hoessler

Resolution: fixed
Status: newclosed

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

comment:4 Changed 8 years ago by Joachim Hoessler

Thanks for the patch!

Modify Ticket

as closed The owner will remain Joachim Hoessler.
The resolution will be deleted. Next status will be 'reopened'.

Add Comment

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

Note: See TracTickets for help on using tickets.