diff options
Diffstat (limited to 'rt/docs/customizing')
-rw-r--r-- | rt/docs/customizing/approvals.pod | 191 | ||||
-rw-r--r-- | rt/docs/customizing/lifecycles.pod | 478 | ||||
-rw-r--r-- | rt/docs/customizing/search_result_columns.pod | 180 | ||||
-rw-r--r-- | rt/docs/customizing/styling_rt.pod | 169 |
4 files changed, 1018 insertions, 0 deletions
diff --git a/rt/docs/customizing/approvals.pod b/rt/docs/customizing/approvals.pod new file mode 100644 index 000000000..af5aa3b0a --- /dev/null +++ b/rt/docs/customizing/approvals.pod @@ -0,0 +1,191 @@ +=head1 RT Approvals + +Some types of change requests processed through RT can +require an approval before being fulfilled. You can configure +RT to set up such an approval workflow for tickets in +queues you select. + +This document walks through the steps to set up a +"Change requests" queue with approvals. You should try +this in a test instance first. If you don't have a test RT +instance, you should read through the entire document first, +change the details as needed for your approval scenario, and then +set up approvals. + +=head2 Overview + +The approvals solution in RT involves using a special queue, +called ___Approvals, to hold approval requests. Scrips and +templates automatically create the necessary tickets +and process the approval or rejection. + +=head2 Change Management Queue + +Since this example will use a change management queue as the +queue where tickets need approval, first we'll set up the queue. + +Login into UI as the 'root' user. Go to Tools -> Configuration -> +Queues and create a new 'Change requests' queue. + +When you set up this queue, do not select the "approvals" Lifecycle. +That selection is for the ___Approvals queue itself, not for queues that +need tickets approved. + +=head3 Change Management Template + +Once the Change Management queue is created, select Templates +-> Create in the queue configuration menu. Enter the Name 'create approval', +leave the default Type as Perl and in the content area enter the following: + + ===Create-Ticket: Manager approval + Subject: Manager Approval for {$Tickets{TOP}->Id} - {$Tickets{TOP}->Subject} + Depended-On-By: TOP + Queue: ___Approvals + Owner: root + Requestors: {$Tickets{TOP}->RequestorAddresses} + Type: approval + Content-Type: text/plain + Due: {time + 3*24*60*60} + Content: Please approve me. + + Thanks. + ENDOFCONTENT + +All of the text should be against the left side of the textarea +with no spaces. + +Click create. + +You'll now use this template when you create the scrip. + +=head3 Change Management Scrip + +Now you need a scrip. On the queue configuration page, select +Scrips -> Create. For the Description, enter 'Create an approval +on ticket create', select the 'On Create' condition, 'Create Tickets' +action, and select the template you just created. Click create. + +=head3 Testing + +You can already test your first workflow with approvals. Create +a ticket in your new 'Change requests' queue. You're logged in as +'root' and the owner of the approval is root (based on the template), +so it's your job to approve or deny the request. Select Tools -> Approvals +in the RT main menu. You should see your first approval request. + +Select the 'Deny' radio button, write 'too expensive' in the notes area +and click Go! You just rejected the approval request. If you open the ticket +you created for testing then you will see that it's rejected +as well and has the correspondence: + + Greetings, + + Your ticket has been rejected by root. + + Approver's notes: too expensive + +You may need to search for the ticket since the rejected state means +it's no longer 'active'. + +Where did this message come from? From templates in the ___Approvals +queue. + +=head2 ___Approvals queue + +___Approvals is a special queue where all approvals are created. The queue +is disabled and is not shown in until you search for it. +Go to Tools -> Configuration -> Queues, leave "Name is" in the search +area and enter ___Approvals into the search +field. Check 'Include disabled queues in listing.' and click Go! +You should now see the ___Approvals queue configuration page. + +You may want to change the name of the ___Approvals queue, but parts of RT +expect it not to change. The name normally isn't shown to users, however, so +it will be largely invisible. + +=head2 Approvals' templates + +From the ___Approvals queue configuration page, click 'Templates' in the +page menu. You should see templates that are used after actions +on approvals. For example if you click on the 'Approval Rejected' +template in the list, you will see the template that generates +the correspondence mentioned above. + +=over 4 + +=item * New Pending Approval + +Owners of new approval requests get this message. + +=item * Approval Passed + +Recorded as correspondence on the ticket when it's approved by an +approver, but still requires more people to approve. + +=item * All Approvals Passed + +Recorded when no more approvals are required. + +=item * Approval Rejected + +Recorded when the approval request is rejected (denied). + +=item * Approval Ready for Owner + +Sent to the Owner of the ticket when it's approved and no more approvals +are required. + +=back + +You can customize these templates to meet your needs. However, +note that there is just one ___Approvals queue for the system, +so make sure changes work with all queues that use approvals. + +=head2 Approvers + +Navigate back to the template used to create approvals. It has +the following line: + + Owner: root + +With this code you set the owner of the approval request to root. +Approvals, as well as tickets, have Ccs, AdminCcs and Requestors. For +example the following line copies requestors from the Tickets +to the approval request: + + Requestors: {$Tickets{TOP}->RequestorAddresses} + +Let's create a group 'Change Approvers' and let any user of +this group approve 'Change Requests'. Create the group, and add root +as a member. Open the 'create an approval' template, and replace +the 'Owner:...' line with the following: + + AdminCcGroup: Change Approvers + +Note that this line only works in RT 4.0.5 and newer. + +Create another test ticket, and you as root still should be able to see +the newly created approval, but now because of the group membership. +You can accept or deny it. + +Any member of the group can accept/deny without consulting +the other members, which is useful with more complex +multistep workflows. + +=head2 Approvers' Rights + +Since the ___Approvals queue is a regular RT queue, you need +to grant rights to allow your approvers to operate on approval +requests. As root, you have super user rights and haven't needed +specific rights for this example. + +It's wise to grant rights via roles as there +is only one queue for all approvals in the system. + +To grant rights to your Change Approvers group, go to the queue +configuration page for the ___Approvals queue. Click on Group Rights +in the page menu. Grant ShowTicket and ModifyTicket rights to the +Owner and AdminCc roles. This should be enough for most cases. + +Now members of the 'Change Approvers' group can act on approvals +even if they have no SuperUser rights. diff --git a/rt/docs/customizing/lifecycles.pod b/rt/docs/customizing/lifecycles.pod new file mode 100644 index 000000000..76e60003a --- /dev/null +++ b/rt/docs/customizing/lifecycles.pod @@ -0,0 +1,478 @@ +=head1 Ticket Lifecycles + +By default, RT comes with ticket statuses that work for many types +of workflows: new, open, stalled, resolved, rejected, and deleted. +But there can be any number of workflows where these status values +don't completely fit. RT allows you to add new custom status values and +define their behavior with a feature called Lifecycles. + +=head1 Adding a New Status + +Because Statuses are controlled via lifecycles, you must manipulate the entire +lifecycle configuration to add a status. In earlier versions of RT new statuses +could be added by adding a new element to an array in RT's config file. But +because lifecyles are built around statuses, the entire lifecycle configuration +must be modified even if you only need new statuses. + +=head2 Copy Lifecycle Config + +First, copy the C<%Lifecycles> hash from C<RT_Config.pm> and paste it into +C<RT_SiteConfig.pm>. + +=head2 Add Status Value + +Add the status to the set where your new status belongs. This example adds +C<approved> to the active statuses: + + active => [ 'open', 'approved', 'stalled' ], + +=head2 Update Transitions + +Now the transitions section must be updated so that the new status can +transition to the existing statuses and also so the existing statuses can +transition to the new status. + + new => [qw( open approved stalled resolved rejected deleted)], + open => [qw(new approved stalled resolved rejected deleted)], + approved => [qw(new open stalled resolved rejected deleted)], + stalled => [qw(new open approved rejected resolved deleted)], + resolved => [qw(new open approved stalled rejected deleted)], + rejected => [qw(new open approved stalled resolved deleted)], + deleted => [qw(new open approved stalled rejected resolved )], + +=head1 Order Processing Example + +This guide demonstrates lifecycles using an order fulfillment +system as a real-world example. You can find full lifecycles +documentation in L<RT_Config/Lifecycles>. + +As with all RT custom configuration, if you are customizing the RT +lifecycle, make your changes in your C<RT_SiteConfig.pm> file, not +directly in C<RT_Config.pm>. If you are adding a new lifecycle, you can +add a new entry with: + + Set(%Lifecycles, my_new_lifecycle => { ... } ); + +The detailed configuration options are discussed below. Once you add it +and restart the server, the new lifecycle will be available on the +queue configuration page. + +To show how you might use custom lifecycles, we're going to configure +an RT lifecycle to process orders of some sort. In our order example, +each ticket in the queue is considered a separate order and the orders +have the following statuses: + +=over + +=item pending + +The order just came in untouched, pending purchase validation + +=item processing + +The order is being looked at for transaction processing + +=item delivery + +The order is out for delivery + +=item delivered + +The order was successfully delivered to its destination + +=item refunded + +The order was delivered but subsequently refunded + +=item declined + +There was an error in the process validation and the order was denied purchase + +=back + +In this particular example, the only status an order can start with is +'pending.' When a process coordinator chooses to take this order, it +goes into processing. The order can then either be delivered or denied +processing. Once denied, the lifecycle for that order ends. If it is +delivered, the order can still be refunded. + +The following sections walk through each part of the configuration. +You can find the full configuration at the end in case you want to +see the exact syntax or use it to experiment with. + +=head2 Defining Status Values + +Every queue has a lifecycle assigned to it. Without changing any +configuration, you are given two lifecycles to choose from: "default" +and "approvals." The approvals lifecycle is used by the internal +approvals queue, and should not be changed or used by other queues. Do +not modify the approvals lifecycle unless you fully understand how RT +approvals work. + +=for html <img alt="Lifecycle choices" src="../images/lifecycle-choices.png"> + +=for :text [Lifecycle choices F<docs/images/lifecycle-choices.png>] + +=for :man [Lifecycle choices F<docs/images/lifecycle-choices.png>] + +In RT 4.0, the C<@ActiveStatus> and C<@InactiveStatus> configurations +which were previously available are gone. The logic defined by those +options is now a subset of RT's lifecycle features, as described here. + +A ticket naturally has three states: initial (I<new>), active (I<open> and +I<stalled>), and inactive (I<resolved>, I<rejected>, and I<deleted>). These +default settings look like this in the C<RT_Config.pm> file: + + default => { + initial => [ 'new' ], + active => [ 'open', 'stalled' ], + inactive => [ 'resolved', 'rejected', 'deleted' ], + +The initial state is the default starting place for new tickets, although +you can create tickets with other statuses. Initial is generally used +to acknowledge that a request has been made, but not yet acted on. RT +sets the Started date on a ticket when it is moved out of the initial state. + +Active tickets are currently being worked on, inactive tickets have reached +some final state. By default, inactive tickets don't show up in search +results. The AutoOpen action sets a ticket's status to the first active +status. You can find more details in L<RT_Config/"Lifecycle definitions">. + +Now we want to set up some statuses appropriate for order fulfillment, +so we create a new top-level key called C<orders> and add our new status +values. + + Set( %Lifecycles, orders => { + initial => [ 'pending' ], + active => [ 'processing', 'delivery' ], + inactive => [ 'delivered', 'returned', 'declined', 'deleted' ], + # ..., + }); + +We still use the initial, active and inactive categories, but we are +able to define status values that are appropriate for the workflow +we want to create. This should make the system more intuitive for users. + +=head2 Transitions + +The typical lifecycle follows the path initial -> active -> inactive. +Obviously the path of a ticket can get more complicated than this, which +is where transitions come into play. + +Transitions manage the flow of a ticket from status to status. This +section of the configuration has keys, which are the current status, +and values that define which other statuses the ticket can transition +to. Here are the transitions we define for our order process. + + Set( %Lifecycles, orders => { + # ..., + transitions => { + '' => [qw(pending processing declined)], + pending => [qw(processing declined deleted)], + processing => [qw(pending declined delivery delivered deleted)], + delivery => [qw(pending delivered returned deleted)], + delivered => [qw(pending returned deleted)], + returned => [qw(pending delivery deleted)], + deleted => [qw(pending processing delivered delivery returned)], + }, + # ..., + }); + +If a ticket is in the delivered status, it doesn't make sense for it to +transition to processing or declined since the customer already has the +order. However, it can transition to returned since they could send it back. +The configuration above defines this for RT. + +The C<''> entry defines the valid statuses when a ticket is created. + +Deleted is a special status in RT that allows you to remove a ticket from +active use. You may need to do this if a ticket is created by mistake, or +a duplicate is created. Once deleted, a ticket will never show up in search +results. As you can see, the system will allow you to +transition to deleted from any status. + +=head2 Rights and Access Control + +Your workflow may have several people working on tickets at different +steps, and for some you may want to make sure only certain users +can perform certain actions. For example, the company may have a rule +that only the quality assurance team is allowed to approve (or decline) +an order for delivery. + +You can apply labels to transitions and assign rights to them to allow +you to apply this sort of access control. This is done with a rights +entry: + + Set( %Lifecycles, orders => { + # ..., + rights => { + '* -> declined' => 'DeclineOrder', + '* -> delivery' => 'ApproveOrder', + }, + # ..., + }); + +This configuration tells RT to require the right DeclineOrder for a +transition from any status (C<*>) to C<declined>. The ApproveOrder +right is similar, but for C<delivery>. These rights take the place of +the standard ModifyTicket right, not in addition to it, so keep that +in mind when creating and assigning new rights. + +Once these rights are configured and loaded (by restarting the web +server), they can be assigned in the web UI to groups, queues, and users. +The rights show up on the rights pages in a Status tab alongside the +standard RT rights tabs. + +=for html <img alt="Lifecycle group rights" src="../images/global-lifecycle-group-rights.png"> + +=for :text [Lifecycle group rights F<docs/images/global-lifecycle-group-rights.png>] + +=for :man [Lifecycle group rights F<docs/images/global-lifecycle-group-rights.png>] + +After a status transition right is granted, users with the right will see +the status in the drop-down, and possibly any related actions (see +L</Actions>). + +=head2 Default Status + +There are interfaces to RT from which it isn't possible to define a status, +like sending an email to create a ticket, but tickets +require a status. To handle these cases, you can set +default status values for RT to use when the user doesn't explicitly set +a value. + +Looking at the defaults section in the standard RT configuration, +you can see the events for which you can define a default status. +For example, 'on_create' => 'new' automatically gives newly created tickets +a C<new> status when the requestor doesn't supply a status. We can do the same +for our process. + + Set( %Lifecycles, orders => { + defaults => { + on_create => 'pending', + }, + # ..., + }); + +Only a small number of defaults are needed because in practice there are +relatively few cases where a ticket will find itself without a status or +in an ambiguous state. + +=head2 Actions + +To customize how transitions are presented in RT, lifecycles have an +C<actions> section where you can customize how an action (e.g. changing +status from new -> open) looks and functions. You can customize the action's +label, which is how it appears to users, and the type of update, either comment +or reply. As an example, in the default RT configuration the action +"new -> open" has the default label "Open it" and an update value of C<Respond>. + +Using the lifecycles configuration, you can change the label to anything you +like. You can set the update option to C<Comment> or C<Respond>, which tells RT +to process the action as a comment (not sent to requestors) or a reply (sent +to requestors). + +This part of the lifecycles configuration replaces the previous +C<$ResolveDefaultUpdateType> configuration value. To mimic that option, set +the update type to C<Comment> for all transitions to C<resolved>. + +Here is an example of a change we might make for our order process: + + Set( %Lifecycles, orders => { + # ..., + actions => [ + 'pending -> processing' => { + label => 'Open For Processing', + update => 'Comment', + }, + 'pending -> declined' => { + label => 'Decline', + update => 'Respond', + }, + # ... + ], + # ... + }); + +Alternatively, supplying no update type results in a "quick" +action that changes the status immediately without going through the +ticket update page. RT's default "Delete" action is a "quick" action, +for example: + + # from the RT "default" lifecycle + 'new -> deleted' => { + label => 'Delete', + }, + +If the transition has an associated right, it must be granted for a user to +see the action. For example, if we give a group the DeclineOrder right as +shown in the earlier example, members of that group will see a Decline option +in their Actions menu if a ticket has a pending status. The +L</"Full Configuration"> at the end shows other action entries that +make the Decline option available in more cases. + +=for html <img alt="Action menu decline" src="../images/action-decline.png"> + +=for :text [Action menu decline F<docs/images/action-decline.png>] + +=for :man [Action menu decline F<docs/images/action-decline.png>] + +=head2 Mapping Between Queues + +As we've demonstrated, each queue can have its own custom lifecycle, but +in RT you sometimes want to move a ticket from one queue to another. +A ticket will have a status in a given queue, but that status may not +exist in another queue you want to move the ticket to, or it may exist +but mean something different. To allow tickets to move between queues with +different lifecycles, RT needs to know how to set the status appropriately. + +The lifecycle configuration has a C<__maps__> entry to allow you to +specify the mappings you want between different queues. Sometimes statuses +between queues don't or can't match perfectly, but if you need to move +tickets between those queues, it's important that you provide a complete +mapping, defining the most sensible mapping you can. + +If you don't provide a mapping, users will see an error when they try to +move a ticket between queues with different lifecycles but no mapping. + + Set( %Lifecycles, orders => { + # ..., + __maps__ => { + 'default -> orders' => { + 'new' => 'pending', + 'open' => 'processing', + # ..., + }, + 'orders -> default' => { + 'pending' => 'new', + 'processing' => 'open', + # ..., + }, + # ..., + }, + # ..., + }); + +In the example above, we first define mappings between the default queue and +our new orders queue. The second block defines the reverse for tickets that +might be moved from the orders queue to a queue that uses the default lifecycle. + +=head2 Full Configuration + +Here is the full configuration if you want to add it to your RT instance +to experiment. + + Set(%Lifecycles, + + # 'orders' shows up as a lifecycle choice when you create a new + # queue or modify an existing one + orders => { + # All the appropriate order statuses + initial => [ 'pending' ], + active => [ 'processing', 'delivery' ], + inactive => [ 'delivered', 'returned', 'declined' ], + + # Default order statuses for certain actions + defaults => { + on_create => 'pending', + }, + + # Status change restrictions + transitions => { + '' => [qw(pending processing declined)], + pending => [qw(processing declined deleted)], + processing => [qw(pending declined delivery delivered deleted)], + delivery => [qw(pending delivered returned deleted)], + delivered => [qw(pending returned deleted)], + returned => [qw(pending delivery deleted)], + deleted => [qw(pending processing delivered delivery returned)], + }, + + # Rights for different actions + rights => { + + # These rights are in the default lifecycle + '* -> deleted' => 'DeleteTicket', + '* -> *' => 'ModifyTicket', + + # Maybe we want to create rights to keep QA rigid + '* -> declined' => 'DeclineOrder', + '* -> delivery' => 'ApproveOrder', + }, + + # Actions for the web UI + actions => [ + 'pending -> processing' => { + label => 'Open For Processing', + update => 'Comment', + }, + 'pending -> delivered' => { + label => 'Mark as being delivered', + update => 'Comment', + }, + 'pending -> declined' => { + label => 'Decline', + update => 'Respond', + }, + 'pending -> deleted' => { + label => 'Delete', + }, + 'processing -> declined' => { + label => 'Decline', + update => 'Respond', + }, + 'processing -> delivery' => { + label => 'Out for delivery', + update => 'Comment', + }, + 'delivery -> delivered' => { + label => 'Mark as delivered', + update => 'Comment', + }, + 'delivery -> returned' => { + label => 'Returned to Manufacturer', + update => 'Respond', + }, + 'delivered -> returned' => { + label => 'Returned to Manufacturer', + update => 'Respond', + }, + 'returned -> delivery' => { + label => 'Re-deliver Order', + update => 'Respond', + }, + 'deleted -> pending' => { + label => 'Undelete', + update => 'Respond', + }, + ], + }, + + # Status mapping different different lifecycles + __maps__ => { + 'default -> orders' => { + 'new' => 'pending', + 'open' => 'processing', + 'stalled' => 'processing', + 'resolved' => 'delivered', + 'rejected' => 'declined', + 'deleted' => 'deleted', + }, + 'orders -> default' => { + 'pending' => 'new', + 'processing' => 'open', + 'delivered' => 'resolved', + 'returned' => 'open', # closest matching we have in 'default' + 'declined' => 'rejected', + 'deleted' => 'deleted', + }, + }, + ); + +Here is an example history of a ticket following this lifecycle: + +=for html <img alt="Lifecycle history" src="../images/order-history-example.png"> + +=for :text [Lifecycle history F<docs/images/order-history-example.png>] + +=for :man [Lifecycle history F<docs/images/order-history-example.png>] diff --git a/rt/docs/customizing/search_result_columns.pod b/rt/docs/customizing/search_result_columns.pod new file mode 100644 index 000000000..7eef416a7 --- /dev/null +++ b/rt/docs/customizing/search_result_columns.pod @@ -0,0 +1,180 @@ +=head1 RT Search Results + +Ticket search results in RT are presented as a table with multiple heading +rows, one for each element of ticket metadata you have selected. Each +row in the table represents one ticket and the appropriate metadata is +displayed in each column. You can see similar listings when you search +for other objects in RT like users, queues, templates, etc. + +For tickets, the Query Builder allows you to modify the column layout using +the Sorting and Display Columns sections at the bottom of the page. With +them you can add and remove data elements to sort by, change the sort order, +and add and remove which columns you want to see. + +Although the Add Columns box has an extensive list of available columns, there +are times when you need a value not listed. Sometimes what you want is a +value calculated based on existing ticket values, like finding the difference +between two date fields. RT provides a way to add this sort of customization +using something called a Column Map. + +=head2 Level of Difficulty + +The customizations described in this section require administrative access +to the RT server and the RT filesystem, typically root or sudo level access. +The customizations involve adding new code to RT, which is written in the +L<Perl|http://www.perl.org/> programming language and uses the +L<Mason|http://www.masonbook.com/> templating system. If you follow the example +closely, you should be able to set up simple column maps with a basic +understanding of these. For more complicated configurations, you may need +to do more research to understand the Perl and Mason syntax. + +=head2 Column Maps + +Each column in a ticket listing gets run through a bit of code called a +Column Map that allows you to perform transformations on the value before +it is displayed. In some cases, the value is just passed through. In others, +like DueRelative, a date is transformed to a relative time like "2 days ago." +You can tap into this functionality to add your own transformations or even +generate completely new values. + +To add to the existing Column Maps, you can use RT's callback +mechanism. This allows you to add code to RT without modifying the core files, +making upgrades much easier. As an example, we'll add a Column Map to the +ticket display and explain the necessary callbacks. You can read more about +callbacks in general in the L<writing_extensions/Callbacks> documentation. + +For our example, let's assume we want to display a response time column that +shows the difference between when a ticket is created and when someone +starts working on it (started date). The two initial values are already +available on the ticket, but it would be convenient to display the +calculated value in our search. + +=head2 Column Map Callback + +First we need to determine where to put our callback. RT's core Column Map code +for tickets is here: + + share/html/Elements/RT__Ticket/ColumnMap + +We'll look there first, both to see some sample Column Maps and also to look +for an appropriate callback to use to add our own. Looking in that file, +we see C<$COLUMN_MAP>, which is a large hashref with entries for each of the +items you see in the Add Columns section of the Query Builder. That's where +we need to add our new Column Map. + +Looking in the C<init> section, we find a callback with a C<CallbackName> +"Once" and it passes the C<$COLUMN_MAP> reference as an argument, so that's +the callback we need. + +Following the callback documentation, we determine we can put our callback +here: + + local/html/Callbacks/MyRT/Elements/RT__Ticket/ColumnMap/Once + +where F<Once> is the name of the file where we'll put our code. + +In the F<Once> file, we'll put the following code: + + <%init> + $COLUMN_MAP->{'TimeToFirstResponse'} = { + title => 'First Response', # loc + attribute => 'First Response', + value => sub { + my $ticket = shift; + return $ticket->StartedObj->DiffAsString($ticket->CreatedObj); + } + }; + </%init> + <%args> + $COLUMN_MAP + </%args> + +Starting with the C<args> section, the value we're interested in is +the C<$COLUMN_MAP> hash reference. Since it's a reference, it's pointing +to the actual data structure constructed in the core RT code. This means +we can add more entries and RT will have access to them. + +=head2 Column Map Parameters + +As you can see in the examples in the core F<ColumnMap> file, each entry +has a key and a hashref with several other parameters. The key needs to be a +unique value. If you using an existing value, you'll overwrite the original +values. + +The parameters in the hashref are as follows: + +=over + +=item title + +The title is what will be used in the header row to identify this value. +The C<# loc> is some special markup that allows RT to replace the value +with translations in other languages, if they are available. + +=item attribute + +This defines the value you can use to reference your new column map +from an RT Format configuration. You can edit formats in the Query +Builder's Advanced section. If you're not familiar with formats, it's +usually safe to set the attribute to the same value as C<title>. It should +be descriptive and unique. + +=item value + +This is where you can put code to transform or calculate the value that +will be displayed. This sets the value you see in the search results +for this column. + +=back + +=cut + +Each of these can be a value like a simple string or an anonymous +subroutine with code that runs to calculate the value. + +If you write a subroutine, as we do for C<value> in our example, RT will +pass the current object as the first parameter to the sub. Since +we're creating a column map for tickets, as RT processes the ticket for +each row in the search results, the ticket object for that ticket is made +available as the first parameter to our subroutine. + +This allows us to then call methods on the L<RT::Ticket> object to access +and process the value. In our case, we can get the L<RT::Date> objects for +the two dates and use the L<RT::Date/DiffAsString> method to calculate and +return the difference. + +When writing code to calculate values, remember that it will be run for each +row in search results. You should avoid doing things that are too time +intensive in that code, like calling a web service to fetch a value. + +=head2 Adding to Display Columns + +Now that we have our column map created, there is one more callback to add +to make it available for all of our users in the Add Columns section in +the Query Builder. This file builds the list of fields available: + + share/html/Search/Elements/BuildFormatString + +Looking there, we see the default callback (the callback without an +explicit C<CallbackName>) passes the C<@fields> array, so that will work. +Create the file: + + local/html/Callbacks/MyRT/Search/Elements/BuildFormatString/Default + +And put the following code in the F<Default> file: + + <%INIT> + push @{$Fields}, 'TimeToFirstResponse'; + </%INIT> + <%ARGS> + $Fields => undef + </%ARGS> + +This puts the hash key we chose for our column map in the fields list so it +will be available in the list of available fields. + +=head2 Last Steps + +Once you have the code in place, stop the RT web server, clear the Mason +cache, and restart the server. Watch the RT logs for any errors, and +navigate to the Query Build to use your new column map. diff --git a/rt/docs/customizing/styling_rt.pod b/rt/docs/customizing/styling_rt.pod new file mode 100644 index 000000000..c5802a84b --- /dev/null +++ b/rt/docs/customizing/styling_rt.pod @@ -0,0 +1,169 @@ +=head1 Customizing the Look of Your RT + +While the default RT color scheme nicely matches the Best Practical colors, +you may want to personalize your RT instance to make it better fit with +your company colors. + + +=head1 Selecting a Theme + +The fundamental look of RT comes from the selected theme. Different +RT versions have a default, and the RT admin can set the system-wide +theme with the C<$WebDefaultStylesheet> configuration value in the +F<RT_SiteConfig.pm> file. + +RT 4.0 comes with the following themes: + +=over + +=item web2 + +An approximation of the 3.8 style + +=item aileron + +The default layout for RT 4.0 + +=item ballard + +Theme which doesn't rely on JavaScript for menuing + +=back + +If you have granted the ModifySelf right to users on your system, +they can pick a different theme for themselves by going to +Logged in as -> Settings -> Options and selecting a different theme. + + +=head1 RT Theme Editor + +RT has some built-in controls to manage the look of the theme you select. +To use the Theme Editor, log in as a SuperUser (like root), and navigate +to Tools -> Configuration -> Tools -> Theme. + +=for html <img alt="RT theme editor, defaults" src="../images/theme_editor_defaults.png"> + +=for :text [RT theme editor image at F<docs/images/theme_editor_defaults.png>] + +=for :man [RT theme editor image at F<docs/images/theme_editor_defaults.png>] + +=head2 Logo and Colors + +From there you can upload a logo and pick colors for the various page +sections. RT will automatically pick out the six most frequent primary +colors from your logo and offer them as options next to the color wheel. +In less than a minute, you can upload a logo and set a few colors. + +Until you click "Save", color changes are temporary and are only shown +to you. When you find the color scheme you want, click Save to make it +the new theme for the entire RT instance. If you ever want to wipe the +slate clean, you can use one or both of the "Reset to default" buttons. + +=head2 Basic CSS Customization + +The theme editor lets you do a bit more if you know your way around CSS +or have a web designer who does. By writing your own styles in the +Custom CSS box, you can quickly customize the RT look and feel pretty +extensively. The primary RT elements are stubbed out for you in the +edit box. + +After making CSS changes, click Try to see how they look, and click Save +when you're done. + + +=head1 Advanced CSS Customization + +If you're more ambitious and good at CSS, you can go even further and +create your own theme. As with all modifications to RT, it's a bad idea +to just change the CSS for one of the standard RT themes in place. When +you upgrade, if you protect your modifications from being over-written, +you may miss out on updates that are required for new features. In the +worst case, an upgrade might wipe out all of your changes. + +Below are a few approaches to customizing RT's CSS. + +=head2 Additional files + +RT allows you to conveniently include additional CSS files after the +default CSS styles, via the C<@CSSFiles> configuration option. To add +an extra CSS file, for example F<my-site.css>, create the local overlay +directory: + + $ mkdir -p local/html/NoAuth/css/ + +And place your F<my-site.css> file in it. Finally, adjust your +C<@CSSFiles> in your F<RT_SiteConfig.pm>: + + Set( @CSSFiles, ('my-site.css') ); + +This technique is preferred to callbacks (below) because CSS included +via this way will be minified. It is also included across all styles, +unlike the callback technique. + +If you are writing an extension, see L<RT/AddStyleSheets> for how to +simply and programmatically add values to C<@CSSFiles>. + +=head2 Callbacks + +RT's CSS files are also Mason templates and the main CSS file, +conveniently called C<main.css>, has a C<Begin> and C<End> callback +allowing you to inject custom CSS. + +To create an End callback, create the callback directory and an +End file in that directory: + + $ mkdir -p local/html/Callbacks/MyRT/NoAuth/css/aileron/main.css + $ touch local/html/Callbacks/MyRT/NoAuth/css/aileron/main.css/End + +You can use any name you want for the C<MyRT> directory and the theme +directory should correspond with the theme you want to change. + +RT will now evaluate the contents of that file after it processes all +of the C<@import> statements in C<main.css>. + + +=head1 Designing Your Own Theme + +The above approaches work well if you need to change the look of +part of RT, but you may want to design your own RT theme +and leave the standard RT themes available to users unmodified. In +this case, you'll want to create your own CSS directory. + +As shown above, the C<local> directory is the place to put +local modifications to RT. Run the following commands in your +C</opt/rt4> directory (or wherever your RT is installed) to get +started: + + $ mkdir -p local/html/NoAuth/css/localstyle + $ cp -R share/html/NoAuth/css/aileron/* local/html/NoAuth/css/localstyle/ + +You can call your "localstyle" directory whatever you want and you don't +have to copy the aileron theme to start from, but it's a good place to +start off for RT4. + +Now set C<$WebDefaultStylesheet> in RT_SiteConfig.pm to the new directory +name you selected, for example: + + Set( $WebDefaultStylesheet, 'localstyle' ); + +If you restart your RT it should look just the same (assuming you copied +your current default theme), but if you go to your Options page you'll +see that the system default theme is now your new "localtheme." + +If you look at the CSS being loaded, you'll also see that the main css +file is now being loaded from your local directory. But you'll also see +that files are still being loaded from the main RT css directories as +well. Why? + +The place to start understanding the loading order of RT's CSS is the +C<main.css> file. You'll see it first loads C<..base/main.css> which +are the base styles for RT along with styles for other tools RT uses +like jQuery. After loading all of the base styles, C<main.css> then +imports a theme-specific version with overrides and new style elements +for the selected theme. So as long as you follow the CSS precedence rules +and use the correct specificity, you get the last chance to modify things. + +You can start modifying things by editing the CSS files in your new +localstyle directory. When you upgrade RT, you'll want to look specifically +at any changes to the style you started from to see if there are any new +styles you want to merge into your new style. |