RT#71166: Catch blank intro_duration at package definition setup
[freeside.git] / rt / docs / customizing / search_result_columns.pod
1 =head1 RT Search Results
2
3 Ticket search results in RT are presented as a table with multiple heading
4 rows, one for each element of ticket metadata you have selected. Each
5 row in the table represents one ticket and the appropriate metadata is
6 displayed in each column. You can see similar listings when you search
7 for other objects in RT like users, queues, templates, etc.
8
9 For tickets, the Query Builder allows you to modify the column layout using
10 the Sorting and Display Columns sections at the bottom of the page. With
11 them you can add and remove data elements to sort by, change the sort order,
12 and add and remove which columns you want to see.
13
14 Although the Add Columns box has an extensive list of available columns, there
15 are times when you need a value not listed. Sometimes what you want is a
16 value calculated based on existing ticket values, like finding the difference
17 between two date fields. RT provides a way to add this sort of customization
18 using something called a Column Map.
19
20 =head2 Level of Difficulty
21
22 The customizations described in this section require administrative access
23 to the RT server and the RT filesystem, typically root or sudo level access.
24 The customizations involve adding new code to RT, which is written in the
25 L<Perl|http://www.perl.org/> programming language and uses the
26 L<Mason|http://www.masonbook.com/> templating system. If you follow the example
27 closely, you should be able to set up simple column maps with a basic
28 understanding of these. For more complicated configurations, you may need
29 to do more research to understand the Perl and Mason syntax.
30
31 =head2 Column Maps
32
33 Each column in a ticket listing gets run through a bit of code called a
34 Column Map that allows you to perform transformations on the value before
35 it is displayed. In some cases, the value is just passed through. In others,
36 like DueRelative, a date is transformed to a relative time like "2 days ago."
37 You can tap into this functionality to add your own transformations or even
38 generate completely new values.
39
40 To add to the existing Column Maps, you can use RT's callback
41 mechanism. This allows you to add code to RT without modifying the core files,
42 making upgrades much easier. As an example, we'll add a Column Map to the
43 ticket display and explain the necessary callbacks. You can read more about
44 callbacks in general in the L<writing_extensions/Callbacks> documentation.
45
46 For our example, let's assume we want to display a response time column that
47 shows the difference between when a ticket is created and when someone
48 starts working on it (started date). The two initial values are already
49 available on the ticket, but it would be convenient to display the
50 calculated value in our search.
51
52 =head2 Column Map Callback
53
54 First we need to determine where to put our callback. RT's core Column Map code
55 for tickets is here:
56
57     share/html/Elements/RT__Ticket/ColumnMap
58
59 We'll look there first, both to see some sample Column Maps and also to look
60 for an appropriate callback to use to add our own. Looking in that file,
61 we see C<$COLUMN_MAP>, which is a large hashref with entries for each of the
62 items you see in the Add Columns section of the Query Builder. That's where
63 we need to add our new Column Map.
64
65 Looking in the C<init> section, we find a callback with a C<CallbackName>
66 "Once" and it passes the C<$COLUMN_MAP> reference as an argument, so that's
67 the callback we need.
68
69 Following the callback documentation, we determine we can put our callback
70 here:
71
72     local/html/Callbacks/MyRT/Elements/RT__Ticket/ColumnMap/Once
73
74 where F<Once> is the name of the file where we'll put our code.
75
76 In the F<Once> file, we'll put the following code:
77
78     <%init>
79     $COLUMN_MAP->{'TimeToFirstResponse'} = {
80             title     => 'First Response', # loc
81             attribute => 'First Response',
82             value     => sub {
83                 my $ticket = shift;
84                 return $ticket->StartedObj->DiffAsString($ticket->CreatedObj);
85             }
86     };
87     </%init>
88     <%args>
89     $COLUMN_MAP
90     </%args>
91
92 Starting with the C<args> section, the value we're interested in is
93 the C<$COLUMN_MAP> hash reference. Since it's a reference, it's pointing
94 to the actual data structure constructed in the core RT code. This means
95 we can add more entries and RT will have access to them.
96
97 =head2 Column Map Parameters
98
99 As you can see in the examples in the core F<ColumnMap> file, each entry
100 has a key and a hashref with several other parameters. The key needs to be a
101 unique value. If you using an existing value, you'll overwrite the original
102 values.
103
104 The parameters in the hashref are as follows:
105
106 =over
107
108 =item title
109
110 The title is what will be used in the header row to identify this value.
111 The C<# loc> is some special markup that allows RT to replace the value
112 with translations in other languages, if they are available.
113
114 =item attribute
115
116 This defines the value you can use to reference your new column map
117 from an RT Format configuration. You can edit formats in the Query
118 Builder's Advanced section. If you're not familiar with formats, it's
119 usually safe to set the attribute to the same value as C<title>. It should
120 be descriptive and unique.
121
122 =item value
123
124 This is where you can put code to transform or calculate the value that
125 will be displayed. This sets the value you see in the search results
126 for this column.
127
128 =back
129
130 =cut
131
132 Each of these can be a value like a simple string or an anonymous
133 subroutine with code that runs to calculate the value.
134
135 If you write a subroutine, as we do for C<value> in our example, RT will
136 pass the current object as the first parameter to the sub. Since
137 we're creating a column map for tickets, as RT processes the ticket for
138 each row in the search results, the ticket object for that ticket is made
139 available as the first parameter to our subroutine.
140
141 This allows us to then call methods on the L<RT::Ticket> object to access
142 and process the value. In our case, we can get the L<RT::Date> objects for
143 the two dates and use the L<RT::Date/DiffAsString> method to calculate and
144 return the difference.
145
146 When writing code to calculate values, remember that it will be run for each
147 row in search results. You should avoid doing things that are too time
148 intensive in that code, like calling a web service to fetch a value.
149
150 =head2 Adding to Display Columns
151
152 Now that we have our column map created, there is one more callback to add
153 to make it available for all of our users in the Add Columns section in
154 the Query Builder. This file builds the list of fields available:
155
156     share/html/Search/Elements/BuildFormatString
157
158 Looking there, we see the default callback (the callback without an
159 explicit C<CallbackName>) passes the C<@fields> array, so that will work.
160 Create the file:
161
162     local/html/Callbacks/MyRT/Search/Elements/BuildFormatString/Default
163
164 And put the following code in the F<Default> file:
165
166     <%INIT>
167     push @{$Fields}, 'TimeToFirstResponse';
168     </%INIT>
169     <%ARGS>
170     $Fields => undef
171     </%ARGS>
172
173 This puts the hash key we chose for our column map in the fields list so it
174 will be available in the list of available fields.
175
176 =head2 Last Steps
177
178 Once you have the code in place, stop the RT web server, clear the Mason
179 cache, and restart the server. Watch the RT logs for any errors, and
180 navigate to the Query Build to use your new column map.