summaryrefslogtreecommitdiff
path: root/rt/docs
diff options
context:
space:
mode:
Diffstat (limited to 'rt/docs')
-rw-r--r--rt/docs/backups.pod108
-rw-r--r--rt/docs/customizing/approvals.pod191
-rw-r--r--rt/docs/customizing/lifecycles.pod478
-rw-r--r--rt/docs/customizing/search_result_columns.pod180
-rw-r--r--rt/docs/customizing/styling_rt.pod169
-rw-r--r--rt/docs/initialdata.pod486
6 files changed, 1612 insertions, 0 deletions
diff --git a/rt/docs/backups.pod b/rt/docs/backups.pod
new file mode 100644
index 000000000..648105c66
--- /dev/null
+++ b/rt/docs/backups.pod
@@ -0,0 +1,108 @@
+=head1 BACKUPS
+
+RT is often a critical piece of businesses and organizations. Backups are
+absolutely necessary to ensure you can recover quickly from an incident.
+
+Make sure you take backups. Make sure they I<work>.
+
+There are many issues that can cause broken backups, such as a
+C<max_allowed_packet> too low for MySQL (in either the client or server), or
+encoding issues, or running out of disk space.
+
+Make sure your backup cronjobs notify someone if they fail instead of failing
+silently until you need them.
+
+Test your backups regularly to discover any unknown problems B<before> they
+become an issue. You don't want to discover problems with your backups while
+tensely restoring from them in a critical data loss situation.
+
+=head2 DATABASE
+
+You should backup the entire RT database, although for improved speed and space
+you can ignore the I<data> in the C<sessions> table. Make sure you still get
+the C<sessions> schema, however.
+
+Database specific notes and example backup commands for each database are
+below. Adjust the commands as necessary for connection details such as
+database name (C<rt4> is the placeholder below), user, password, host, etc.
+You should put the example commands into a shell script for backup and setup a
+cronjob. Make sure output from cron goes to someone who reads mail! (Or into
+RT. :)
+
+=head3 MySQL
+
+ ( mysqldump rt4 --tables sessions --no-data; \
+ mysqldump rt4 --ignore-table rt4.sessions --single-transaction ) \
+ | gzip > rt-`date +%Y%M%d`.sql.gz
+
+If you're using a MySQL version older than 4.1.2 (only supported on RT 3.8.x
+and older), you should be also pass the C<--default-character-set=binary>
+option to the second C<mysqldump> command.
+
+The dump will be much faster if you can connect to the MySQL server over
+localhost. This will use a local socket instead of the network.
+
+If you find your backups taking far far too long to complete (this point should
+take quite a long time to get to on an RT database), there are some alternate
+solutions. Percona maintains a highly regarded hot-backup tool for MySQL
+called L<XtraBackup|http://www.percona.com/software/percona-xtrabackup/>. If
+you have more resources, you can also setup replication to a slave using binary
+logs and backup from there as necessary. This not only duplicates the data,
+but lets you take backups without putting load on your production server.
+
+=head3 PostgreSQL
+
+ ( pg_dump rt4 --table=sessions --schema-only; \
+ pg_dump rt4 --exclude-table=sessions ) \
+ | gzip > rt-`date +%Y%M%d`.sql.gz
+
+=head2 FILESYSTEM
+
+You will want to back up, at the very least, the following directories and files:
+
+=over 4
+
+=item /opt/rt4
+
+RT's source code, configuration, GPG data, and plugins. Your install location
+may be different, of course.
+
+You can omit F<var/mason_data> and F<var/session_data> if you'd like since
+those are temporary caches. Don't omit all of F<var/> however as it may
+contain important GPG data.
+
+=item Webserver configuration
+
+Often F</etc/httpd> or F</etc/apache2>. This will depend on your OS, web
+server, and internal configuration standards.
+
+=item /etc/aliases
+
+Your incoming mail aliases mapping addresses to queues.
+
+=item Mail server configuration
+
+If you're running an MTA like Postfix, Exim, SendMail, or qmail, you'll want to
+backup their configuration files to minimize restore time. "Lightweight" mail
+handling programs like fetchmail, msmtp, and ssmtp will also have configuration
+files, although usually not as many nor as complex. You'll still want to back
+them up.
+
+The location of these files is highly dependent on what software you're using.
+
+=item Crontab containing RT's cronjobs
+
+This may be F</etc/crontab>, F</etc/cron.d/rt>, a user-specific crontab file
+(C<crontab -l $USER>), or some other file altogether. Even if you only have
+the default cronjobs in place, it's one less piece to forget during a restore.
+If you have custom L<< C<rt-crontool> >> invocations, you don't want to have to
+recreate those.
+
+=back
+
+Simply saving a tarball should be sufficient, with something like:
+
+ tar czvpf rt-backup-`date +%Y%M%d`.tar.gz /opt/rt4 /etc/aliases /etc/httpd ...
+
+Be sure to include all the directories and files you enumerated above!
+
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.
diff --git a/rt/docs/initialdata.pod b/rt/docs/initialdata.pod
new file mode 100644
index 000000000..6445fb0cd
--- /dev/null
+++ b/rt/docs/initialdata.pod
@@ -0,0 +1,486 @@
+=head1 Summary of initialdata files
+
+It's often useful to be able to test configuration/database changes and then
+apply the same changes in production without manually clicking around. It's
+also helpful if you're developing customizations or extensions to be able to
+get a fresh database back to the state you want for testing/development.
+
+This documentation applies to careful and thorough sysadmins as well as
+extension authors who need to make database changes easily and repeatably for
+new installs or upgrades.
+
+=head1 Examples
+
+RT ships with many initialdata files, only one of which is used to
+configure a fresh install; the rest are used for upgrades, but function
+the same despite being named differently.
+
+ etc/initialdata
+ etc/upgrade/*/content
+
+The upgrade "content" files are meant to be incremental changes applied on top
+of one another while the top level initialdata file is for fresh RT installs.
+
+Extensions may also ship with database changes in such files. You may find
+some in your install with:
+
+ find local/plugins -name initialdata -or -name content
+
+=head1 What can be in an initialdata file?
+
+initialdata files are Perl, but often consist primarily of a bunch of data
+structures defining the new records you want and not much extra code. There's
+nothing stopping you from writing a bunch of code, however!
+
+The basic template of a new initialdata file should look something like this:
+
+ use strict;
+ use warnings;
+
+ our @Queues = (
+ # some definitions here
+ );
+
+ our @Groups = (
+ # some other definitions here
+ );
+
+ 1;
+
+The C<@Queues> and C<@Groups> arrays are expected by RT and should contain
+hashref definitions. There are many other arrays RT will look for and act on,
+described below. None are required, all may be used. Keep in mind that since
+they're just normal Perl arrays, you can C<push> onto them from a loop or
+C<grep> out definitions based on conditionals or generate their content with
+C<map>, etc.
+
+The complete list of possible arrays which can be used, along with
+descriptions of the values to place in them, is below.
+
+=head2 C<@Users>
+
+ push @Users, {
+ Name => 'john.doe',
+ Password => 'changethis',
+ Language => 'fr',
+ Timezone => 'America/Vancouver',
+ Privileged => 1,
+ Disabled => 0,
+ };
+
+Each hashref in C<@Users> is treated as a new user to create and passed
+straight into C<< RT::User->Create >>. All of the normal user fields are
+available, as well as C<Privileged> and C<Disabled> (both booleans) which will
+do the appropriate internal group/flag handling.
+
+For a full list of fields, read the documentation for L<RT::User/Create>.
+
+=head2 C<@Groups>
+
+ push @Groups, {
+ Domain => 'UserDefined',
+ Name => 'Example Employees',
+ Description => 'All of the employees of my company',
+ };
+
+Creates a new L<RT::Group> for each hashref. In almost all cases you'll want
+to follow the example above to create a group just as if you had done it from
+the admin interface. B<Do not> omit the C<< Domain => 'UserDefined' >> line.
+
+Additionally, the C<MemberOf> field is specially handled to make it easier to
+add the new group to other groups. C<MemberOf> may be a single value or an
+array ref. Each value should be a user-defined group name or hashref to pass
+into L<< RT::Group->LoadByCols >>. Each group found will have the new group
+added as a member.
+
+Unfortunately you can't specify the I<members> of a group at this time. As a
+workaround, you can push a subref into C<@Final> which adds members to your new
+groups. An example, using a convenience function to avoid repeating yourself:
+
+ push @Final, sub {
+ add_members('My New Group Name' => qw(trs alex ruslan));
+ add_members('My Second Group' => qw(jesse kevin sunnavy jim));
+ };
+
+ sub add_members {
+ my $group_name = shift;
+ my @members = @_;
+
+ my $group = RT::Group->new( RT->SystemUser );
+ $group->LoadUserDefinedGroup($group_name);
+
+ if ($group->id) {
+ for my $name (@members) {
+ my $member = RT::User->new( RT->SystemUser );
+ $member->LoadByCols( Name => $name );
+
+ unless ($member->Id) {
+ RT->Logger->error("Unable to find user '$name'");
+ next;
+ }
+
+ my ($ok, $msg) = $group->AddMember( $member->PrincipalObj->Id );
+ if ($ok) {
+ RT->Logger->info("Added member $name to $group_name");
+ } else {
+ RT->Logger->error("Unable to AddMember $name to $group_name: $msg");
+ }
+ }
+ } else {
+ RT->Logger->error("Unable to find group '$group_name'!");
+ }
+ }
+
+=head2 C<@Queues>
+
+ push @Queues, {
+ Name => 'Helpdesk',
+ CorrespondAddress => 'help@example.com',
+ CommentAddress => 'help-comment@example.com',
+ };
+
+Creates a new L<RT::Queue> for each hashref. Refer to the documentation of
+L<RT::Queue/Create> for the fields you can use.
+
+=head2 C<@CustomFields>
+
+ push @CustomFields, {
+ Queue => 0,
+ Name => 'Favorite color',
+ Type => 'FreeformSingle',
+ LookupType => 'RT::Queue-RT::Ticket',
+ };
+
+Creates a new L<RT::CustomField> for each hashref. It is the most complex of
+the initialdata structures. The most commonly used fields are:
+
+=over 4
+
+=item C<Name>
+
+The name of this CF as displayed in RT.
+
+=item C<Description>
+
+A short summary of what this CF is for.
+
+=item C<Queue>
+
+May be a Name or ID. The single queue or array ref of queues to apply this CF
+to. This does not apply when C<LookupType> does not start with C<RT::Queue>.
+
+=item C<Type>
+
+One of the following on the left hand side:
+
+ SelectSingle # Select one value
+ SelectMultiple # Select multiple values
+
+ FreeformSingle # Enter one value
+ FreeformMultiple # Enter multiple values
+
+ Text # Fill in one text area
+ Wikitext # Fill in one wikitext area
+
+ BinarySingle # Upload one file
+ BinaryMultiple # Upload multiple files
+
+ ImageSingle # Upload one image
+ ImageMultiple # Upload multiple images
+
+ Combobox # Combobox: Select or enter one value
+
+ AutocompleteSingle # Enter one value with autocompletion
+ AutocompleteMultiple # Enter multiple values with autocompletion
+
+ Date # Select date
+ DateTime # Select datetime
+
+ IPAddressSingle # Enter one IP address
+ IPAddressMultiple # Enter multiple IP addresses
+
+ IPAddressRangeSingle # Enter one IP address range
+ IPAddressRangeMultiple # Enter multiple IP address ranges
+
+If you don't specify "Single" or "Multiple" in the type, you must specify
+C<MaxValues>.
+
+=item C<LookupType>
+
+Labeled in the CF admin page as "Applies to". This determines whether your CF
+is for Tickets, Transactions, Users, Groups, or Queues. Possible values:
+
+ RT::Queue-RT::Ticket # Tickets
+ RT::Queue-RT::Ticket-RT::Transaction # Transactions
+ RT::User # Users
+ RT::Group # Groups
+ RT::Queue # Queues
+
+Ticket CFs are the most common, meaning C<RT::Queue-RT::Ticket> is the most
+common C<LookupType>.
+
+=item C<RenderType>
+
+Only valid when C<Type> is "Select". Controls how the CF is displayed when
+editing it. Valid values are: C<Select box>, C<List>, and C<Dropdown>.
+
+C<List> is either a list of radio buttons or a list of checkboxes depending on
+C<MaxValues>.
+
+=item C<MaxValues>
+
+Determines whether this CF is a Single or Multiple type. 0 means multiple. 1
+means single.
+
+Make sure to set the C<MaxValues> field appropriately, otherwise you can end up
+with unsupported CF types like a "Select multiple dates" (it doesn't Just
+Work).
+
+You can also use old-style C<Type>s which end with "Single" or "Multiple", for
+example: SelectSingle, SelectMultiple, FreeformSingle, etc.
+
+=item C<Values>
+
+C<Values> should be an array ref (never a single value!) of hashrefs
+representing new L<RT::CustomFieldValue> objects to create for the new custom
+field. This only makes sense for "Select" CFs. An example:
+
+ my $i = 1;
+ push @CustomFields, {
+ Queue => 0, # Globally applied
+ LookupType => 'RT::Queue-RT::Ticket', # for Tickets
+ Name => 'Type of food',
+ Type => 'SelectSingle', # SelectSingle is the same as: Type => 'Select', MaxValues => 1
+ RenderType => 'Dropdown',
+ Values => [
+ { Name => 'Fruit', Description => 'Berries, peaches, tomatos, etc', SortOrder => $i++ },
+ { Name => 'Vegetable', Description => 'Asparagus, peas, lettuce, etc', SortOrder => $i++ },
+ # more values as such...
+ ],
+ };
+
+In order to ensure the same sorting of C<Values>, set C<SortOrder> inside each
+value. A clever way to do this easily is with a simple variable you increment
+each time (as above with C<$i>). You can use the same variable throughout the
+whole file, and don't need one per CF.
+
+=item C<BasedOn>
+
+Name or ID of another Select Custom Field. This makes the named CF the source
+of categories for your values.
+
+=item C<Pattern>
+
+The regular expression text (not C<qr//>!) used to validate values.
+
+=back
+
+Refer to the documentation and implementation of L<RT::CustomField/Create> and
+L<RT::CustomFieldValue/Create> for the full list of available fields and
+allowed values.
+
+=head2 C<@ACL>
+
+C<@ACL> is very useful for granting rights on your newly created records or
+setting up a standard system configuration. It is one of the most complex
+initialdata structures.
+
+=head3 Pick a Right
+
+All ACL definitions expect a key named C<Right> with the internal right name
+you want to grant. The internal right names are visible in RT's admin
+interface in grey next to the longer descriptions.
+
+=head3 Pick a level: on a queue, on a CF, or globally
+
+After picking a C<Right>, you need to specify on what object the right is
+granted. This is B<different> than the user/group/role receiving the right.
+
+=over 4
+
+=item Granted on a custom field by name (or ID), potentially a global or queue
+
+ CF => 'Name',
+
+=item Granted on a queue
+
+ Queue => 'Name',
+
+=item Granted on a custom field applied to a specific queue
+
+ CF => 'Name',
+ Queue => 'Name',
+
+=item Granted globally
+
+Specifying none of the above will get you a global right.
+
+=back
+
+There is currently no way to grant rights on a group or article class level.
+Note that you can grant rights B<to> a group; see below. If you need to grants
+rights on a group or article class level, you'll need to write an C<@Final>
+subref to handle it using the RT Perl API.
+
+=head3 Pick a Principal: User or Group or Role
+
+Finally you need to specify to what system group, system/queue role,
+user defined group, or user you want to grant the right B<to>.
+
+=over 4
+
+=item An internal user group
+
+ GroupDomain => 'SystemInternal',
+ GroupType => 'Everyone, Privileged, or Unprivileged'
+
+=item A system-level role
+
+ GroupDomain => 'RT::System-Role',
+ GroupType => 'Requestor, Owner, AdminCc, or Cc'
+
+=item A queue-level role
+
+ GroupDomain => 'RT::Queue-Role',
+ Queue => 'Name',
+ GroupType => 'Requestor, Owner, AdminCc, or Cc',
+
+=item A group you created
+
+ GroupDomain => 'UserDefined',
+ GroupId => 'Name'
+
+=item Individual user
+
+ UserId => 'Name or email or ID'
+
+=back
+
+=head3 Common cases
+
+You're probably looking for definitions like these most of the time.
+
+=over 4
+
+=item Grant a global right to a group you created
+
+ { Right => '...',
+ GroupDomain => 'UserDefined',
+ GroupId => 'Name' }
+
+=item Grant a queue-level right to a group you created
+
+ { Queue => 'Name',
+ Right => '...',
+ GroupDomain => 'UserDefined',
+ GroupId => 'Name' }
+
+=item Grant a CF-level right to a group you created
+
+ { CF => 'Name',
+ Right => '...',
+ GroupDomain => 'UserDefined',
+ GroupId => 'Name' }
+
+=back
+
+Since you often want to grant a list of rights on the same object/level to the
+same role/group/user, we generally use Perl loops and operators to aid in the
+generation of C<@ACL> without repeating ourselves.
+
+ # Give Requestors globally the right to see tickets, reply, and see the
+ # queue their ticket is in
+ push @ACL, map {
+ {
+ Right => $_,
+ GroupDomain => 'RT::System-Role',
+ GroupType => 'Requestor',
+ }
+ } qw(ShowTicket ReplyToTicket SeeQueue);
+
+=head3 Troubleshooting
+
+The best troubleshooting is often to see how the rights you define in C<@ACL>
+show up in the RT admin interface.
+
+=head2 C<@Scrips>
+
+Creates a new L<RT::Scrip> for each hashref. Refer to the documentation of
+L<RT::Scrip/Create> for the fields you can use.
+
+Additionally, the C<Queue> field is specially handled to make it easier to
+setup the same Scrip on multiple queues:
+
+=over 4
+
+=item Globally
+
+ Queue => 0,
+
+=item Single queue
+
+ Queue => 'General', # Name or ID
+
+=item Multiple queues
+
+ Queue => ['General', 'Helpdesk', 13], # Array ref of Name or ID
+
+=back
+
+=head2 C<@ScripActions>
+
+Creates a new L<RT::ScripAction> for each hashref. Refer to the documentation
+of L<RT::ScripAction/Create> for the fields you can use.
+
+=head2 C<@ScripConditions>
+
+Creates a new L<RT::ScripCondition> for each hashref. Refer to the
+documentation of L<RT::ScripCondition/Create> for the fields you can use.
+
+=head2 C<@Templates>
+
+Creates a new L<RT::Template> for each hashref. Refer to the documentation of
+L<RT::Template/Create> for the fields you can use.
+
+=head2 C<@Attributes>
+
+An array of L<RT::Attribute>s to create. You likely don't need to mess with
+this. If you do, know that the key C<Object> is expected to be an
+L<RT::Record> object on which to call C<AddAttribute>. If you don't provide
+C<Object> or it's undefined, C<< RT->System >> will be used.
+
+=head2 C<@Initial>
+
+=head2 C<@Final>
+
+C<@Initial> and C<@Final> are special and let you write your own processing
+code that runs before anything else or after everything else. They are
+expected to be arrays of subrefs (usually anonymous) like so:
+
+ our @Final = (sub {
+ RT->Logger->info("Finishing up!");
+ });
+
+You have the full power of RT's Perl libraries at your disposal. Be sure to do
+error checking and log any errors with C<< RT->Logger->error("...") >>!
+
+=head1 What's missing?
+
+There is currently no way, short of writing code in C<@Final> or C<@Initial>,
+to easily create B<Classes>, B<Topics>, or B<Articles> from initialdata files.
+
+=head1 Running an initialdata file
+
+ sbin/rt-setup-database --action insert --datafile /path/to/your/initialdata
+
+This may prompt you for a database password.
+
+=head1 Implementation details
+
+All the handling of initialdata files is done in C<< RT::Handle->InsertData >>.
+If you want to know B<exactly> what's happening with each array, your best bet
+is to start reading the code there.
+
+RT takes care of the ordering so that your new queues are created before it
+processes the new ACLs for those queues. This lets you refer to new queues you
+just created by Name.