The rt-liberation Manual
************************

This is the Manual for the rt-liberation system

© 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2020  Free Software Foundation

     Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled ``GNU Free Documentation License''.

Introduction
************

rt-liberation is a GNU/Emacs package for working with the Request Tracker (henceforth abbreviated as just ``RT'') software from Best Practical Solutions. RT has an interactive Web interface, a command line interface (the ``RT CLI''), and a REST interface. rt-liberation uses the RT REST interface to communicate with the RT server.

rt-liberation allows sending search queries to the RT server, browsing the resulting tickets, viewing the tickets' contents and performing operations on the tickets.

Installation
************

rt-liberation can be configured in the ~/.emacs file.

Place rt-liberation in the load path:

     (add-to-list 'load-path "/PATH/TO/rt-liberation/")

Tell GNU/Emacs to load the package with:

     (require 'rt-liberation)

Tell rt-liberation where to find the RT server's REST interface:

     (setq rt-liber-rest-url "rt.example.org")

rt-liberation can issue a command to ``take'' a ticket (that is, assign it to yourself). For this the variable RT-LIBER-USERNAME must be set:

     (setq rt-liber-username "someuser")

rt-liberation can also launch a Web browser to visit a ticket. For that to work the base URL needs to be set in RT-LIBER-BASE-URL. For example:

(setq rt-liber-base-url "https://rt.foo.org/")

Queries
*******

A typical RT server is meant to manage a large amount of tickets. Much more that would be convenient to view all at once. Instead queries are used to view only a subset of the tickets on the server.

rt-liberation has its own Sexp-based query language which maps to RT's TicketSQL language.

Query Compiler
==============

In order to browse and view tickets a list of needs to be requested from the RT server. Typically the tickets answer some kind of criteria, for example ``tickets no older than a week owned by me which have \``foobar\'' in their subject line''. In RT these criteria are formulated with ``TicketSQL'' queries; a structured query language specific to RT.

rt-liberation provides a query compiler function to compile Emacs Lisp symbolic expressions into TicketSQL. The query compiler supports a number of TicketSQL tokens.

Query Language
==============

rt-liberation's Sexp-based query language covers a portion of the TicketSQL language. Here are some of the supported TicketSQL tokens: Boolean tokens as a means of combining query subsections: ``and'', ``or'', ``not''. LIKE attribute tokens: ``subject'', ``content''.

For example here is a query with both Boolean and LIKE tokens:

     (rt-liber-compile-query
         (and (queue "bugs")
              (content "gnu")))

     ==> "Queue = 'bugs' AND Content LIKE 'gnu'"

We can also express negation (note that the compiler produces "!=" and "NOT LIKE" for negation depending on the context):

     (rt-liber-compile-query
      (and (queue "bugs")
           (not (owner "Nobody"))
           (not (content "sprigz"))
           (status "new")))

     ==> "Queue = 'licensing' AND Owner != 'Nobody' \
         AND Content NOT LIKE 'sprigz' AND Status = 'new'"

Attribute tokens which match an attribute to a specific field such as: ``owner'', ``status'' and ``queue''. Temporal tokens which limit the search results to tickets within a certain time interval: ``created'' and ``lastupdated''. Note that temporal keywords such as ``created'' always accept two arguments: BEFORE and AFTER. When either BEFORE or AFTER aren't needed, use NIL instead.

One of the advantages of being able to express the TicketSQL queries as Emacs Lisp is to be able to express queries using Emacs Lisp functions.

Here is a slightly more involved example to illustrate:

     (rt-liber-compile-query
      (and (queue "bugs")
           (owner "me@myaddress.com")
           (status "open")
           (lastupdated nil
                        (format-time-string
                          "%Y-%m-%d"
                          (seconds-to-time
                           (- (time-to-seconds (current-time))
                              (* 60 60 24 7)))))))

     ==> "Queue = 'bugs' AND Owner = 'me@myaddress.com' AND Status = 'open' AND LastUpdated > '2009-03-30'"

Here is an example of how the ticket browser and compiler can be used in function calls:

     (defun rt-liber-display-ticket (ticket-id)
       "Display ticket with TICKET-ID in the ticket-browser."
       (interactive "MTicket ID: ")
       (rt-liber-browse-query
        (rt-liber-compile-query
         (and (queue "complaints")
              (id ticket-id)))))

Ticket Browser
**************

The ticket browser is a special buffer which provides a convenient interface to the results of a server query. The ticket browser can be started by invoking: (rt-liber-browse-query QUERY), where QUERY is a TicketSQL query. The TicketSQL query can be entered manually as a string or as the return value of the query compiler.

 -- Function: rt-liber-browse-query QUERY &optional NEW
     Runs QUERY against the server and launches the browser.

     If NEW is non-nil then the query results will be displayed in a new buffer, otherwise the query results will override the contents of the existing ticket browser buffer. If NEW is a string then that will be the name of the new buffer.

The TicketSQL query can be the return value of the query compiler. For example:

     (rt-liber-browse-query
      (rt-liber-compile-query
          (and (queue "bugs")
               (content "gnu")))

Since the return value of the query compiler is just a TicketSQL string, the following is equivalent:

     (rt-liber-browse-query "Queue = 'bugs' AND Content LIKE 'gnu'")

The ticket browser defines a number of commands:

q
     Bury the ticket browser buffer.

n
     Move point to the next ticket.

p
     Move point to the previous ticket.

RET
     Visit the ticket at point in the see Ticket Viewer.

g
     Refresh the contents of the browser buffer.

G
     Refresh the contents of the browser buffer. Return point to the current ticket after the refresh (if possible).

s
     Mark the ticket as spam.

S
     Delete marked tickets as spam (requires rt-liberation-multi package).

a
     Assign the ticket to a user.

r
     Mark the ticket as ``resolved''.

o
     Mark the ticket as ``open''.

t
     Assign the ticket at point to RT-LIBER-USERNAME.

SPC
     Scroll the text of the ticket browser upward.

DEL
     Scroll the text of the ticket browser downward.

m
     Move the ticket to a different queue.

P
     Set the numerical priority level of the ticket at point.

Ticket Browser Display
======================

The ticket browser displays the tickets in the browser by calling rt-liber-ticketlist-browser-redraw-f which can be changed and customized. Any implementation of rt-liber-ticketlist-browser-redraw-f must leave point at the end of the ticket text.

The ticket data itself can be displayed using rt-liberation ticket format string %-sequences:

%i
     ID number of the ticket in the RT database.

%s
     Subject line.

%c
     Ticket creation time. The format to display the time is specified in the variable RT-LIBER-BROWSER-TIME-FORMAT-STRING.

%S
     Ticket status (``open'', ``new'' etc.)

%r
     Whether the ticket is resolved.

%R
     Requestor/s

%C
     Creator of the ticket.

%o
     Owner of the ticket.

%q
     The queue originating the ticket.

%p
     The numerical priority of the ticket

Here is an example implementation of rt-liber-ticketlist-browser-redraw-f showing the use of the %-sequences. Note the use of text properties to add color to ticket text. The use of text properties as opposed to font-locking is meant to ease customization because otherwise any change in ticket display would break the font-locking regular expressions.

     (defun rt-liber-ticketlist-browser-redraw-f (ticket)
       "Display TICKET."
       (insert (rt-liber-format "[%c] %i" ticket))
       (add-text-properties (point-at-bol)
                            (point-at-eol)
                            '(face rt-liber-ticket-face))
       (newline)
       (insert (rt-liber-format "    [%S] %s" ticket))
       (newline)
       (insert (rt-liber-format "    %o <== %R" ticket)))

The function rt-liber-high-priority-p can be used to apply a different face or text to a ticket if it is high priority. A ticket is considered high priority if its value is strictly higher than RT-LIBER-BROWSER-PRIORITY-CUTOFF

Ticket Browser Sorting
======================

The tickets in the browser are displayed by default in reverse chronological order. Ticket sorting is done by a call to rt-liber-browser-default-sorting-function.

Other sorting orders can be used by binding rt-liber-browser-default-sorting-function to a different function. To ease writing such functions rt-liberation provides two predicate functions to perform comparisons between ticket objects:

 -- Function: rt-liber-lex-lessthan-p a b field
     Return true if A is lexicographically less than B in FIELD.

     Here is an example of sorting tickets lexicographically by owner name using rt-liber-lex-lessthan-p (note that you can feed rt-liber-lex-lessthan-p a date/time string and it will sort it just fine except that it wouldn't make any sense):

          (defun rt-liber-sort-by-owner (ticket-list)
            "Sort TICKET-LIST lexicographically by owner."
            (rt-liber-sort-ticket-list
             ticket-list
             #'(lambda (a b)
                 (rt-liber-lex-lessthan-p a b "Owner"))))

 -- Function: rt-liber-time-lessthan-p a b field
     Return t if A is chronologically less than B in FIELD.

     Here is an example of sorting tickets lexicographically by owner name using rt-liber-time-lessthan-p (note that feeding rt-liber-time-lessthan-p anything but a date/time string, in this case ``Created'' contains a date, will result in an error being signaled).

          (defun rt-liber-sort-by-time-created (ticket-list)
            "Sort TICKET-LIST in reverse chronological order."
            (reverse
             (rt-liber-sort-ticket-list
              ticket-list
              #'(lambda (a b)
                  (rt-liber-time-lessthan-p a b "Created")))))

Ticket Browser Filtering
========================

The Ticket Browser can also filter out (that is, not display) certain tickets based on particular criteria. This probably shouldn't be used instead of a properly formed RT query, but when used in conjunction with correctly formulated queries it becomes a powerful tool.

During ticket display processing the Ticket Browser will call the function pointed to by RT-LIBER-BROWSER-DEFAULT-FILTER-FUNCTION on each ticket, passing the function the ticket alist as a single argument. The function is set by default to rt-liber-default-filter-f, which is a function which will display all tickets and filter none.

If any tickets are filtered, the Ticket Browser will display the filtered ticket count at the bottom ticket listing.

Here is a simple example of how to filter out all of the tickets which have a status of ``deleted''.

First we define a custom filter function. Note how it accepts a single argument, which is the ticket alist, and returns nil if the ticket is to be filtered.

     (defun rt-liber-browser-deleted-filter (ticket)
       (not
        (and ticket
             (string= (cdr (assoc "Status" ticket))
                      "deleted"))))

Then we assign that function to be our default filtering function:

     (setq rt-liber-browser-default-filter-function
           'rt-liber-browser-deleted-filter)

Multiple Ticket Browsers
========================

It is sometimes useful to rename the ticket browser buffer to something more informative than the default RT-LIBER-BROWSER-BUFFER-NAME, especially if there are multiple ticket browsers.

Changing a ticket browser's name can be done normally with `rename-buffer', but it is also possible to name the ticket browser when it is created. In the following example two ticket browser buffers will be created displaying the query results and named ``*updated by supervisor*'' and ``*new tickets*'' respectively:

     (defun rt-liber-daily-rounds ()
       (interactive)

       (rt-liber-browse-query
        (rt-liber-compile-query
         (and (queue "complaints")
              (owner "lem.e.tweakit")
              (status "open")
              (lastupdatedby "molly.manager")))
        "*updated by supervisor*")

       (rt-liber-browse-query
        (rt-liber-compile-query
         (and (queue "complaints")
              (owner "Nobody")
              (status "new")))
        "*new tickets*"))

Ticket Viewer
*************

The ticket viewer is an interface for viewing the contents of a ticket. It provides font-locking to make reading the contents easier via RT-LIBER-VIEWER-FONT-LOCK-KEYWORDS and a number of key-bindings.

The ticket viewer provides key-bindings to help compose emails to send to the RT email interface. The key-bindings for composing email described below are generic, what actually happens when you invoke them depends on the email-backend system you have installed into rt-liberation. rt-liberation-gnus.el provides integration with Gnus, see Gnus Integration.

Setting RT-LIBER-JUMP-TO-LATEST to `t' will cause the viewer to automatically scroll to the latest comment in a ticket when that ticket is visited. By default RT-LIBER-JUMP-TO-LATEST is set to `nil'.

When in the ticket viewer buffer, invoking rt-liber-viewer-take-ticket will ``take'' the ticket.

q
     Bury the ticket viewer buffer.

n
     Move point to the next section in ticket.

N
     Move point to the newest correspondence section, if any.

p
     Move point to the previous section in ticket.

V
     Visit the current ticket in a Web browser.

m
     Compose an answer to the current ticket.

M
     Compose an answer to the current ticket. The content section around point will be inserted into the email body and commented out.

t
     Compose a provisional answer to the current ticket.

T
     Compose a provisional answer to the current ticket. The content section around point will be inserted into the email body and commented out.

F
     Compose an answer to the current ticket. The content section around point will be inserted into the email body verbatim.

c
     Compose a comment for the current ticket.

C
     Comment on the ticket using the current context

g
     Refresh and redisplay the current ticket.

SPC
     Scroll text of ticket viewer upward.

DEL
     Scroll text of ticket viewer downward.

h
     Display the associated ticket in the ticket browser.

Gnus Integration
****************

The file rt-liberation-gnus.el implements integration with Gnus for composing emails. To enable the feature, `require' it after loading rt-liberation:

     (require 'rt-liberation-gnus)

In order for rt-liberation-gnus to be useful a few variables need to be specialized. The following is example code which sets these variables. Below is a thorough description of those variables.

     (setq rt-liber-gnus-comment-address "our-rtserver-comment@ourserver.org"
           rt-liber-gnus-address         "our-rtserver@ourserver.org"
           rt-liber-gnus-subject-name    "ourserver.org")

 -- User Option: rt-liber-gnus-address
     RT-LIBER-GNUS-ADDRESS is the email address which is configured in the RT server email interface for sending a response to the ticket's requestor.

 -- User Option: rt-liber-gnus-comment-address
     RT-LIBER-GNUS-COMMENT-ADDRESS is the email address which is configured in the RT server email interface for adding a comment under the ticket in question.

 -- User Option: rt-liber-gnus-subject-name
     RT-LIBER-GNUS-SUBJECT-NAME is a string, typically included at the beginning of the square brackets in the subject. The string is a part of the subject line which helps the RT server recognize the email.

Gnus posting styles controlled by GNUS-POSTING-STYLES can be customized for rt-liberation-gnus by using the variable RT-LIBER-GNUS-P, which is only non-nil when rt-liberation-gnus launches a Gnus message buffer.

Here is example code which uses RT-LIBER-GNUS-P to override the signature in the default posting style with one special to rt-liberation. Headers can be added and removed in a similar manner.

     (setq gnus-posting-styles
             '((".*"
                (name "Lemm E. Hackitt")
                (address "Lemm@hack.it")
                (signature-file "~/sig.txt")
                ("X-Ethics" "Use GNU"))
               (rt-liber-gnus-p
                (signature-file "~/rt-liber-sig.txt"))))

Once rt-liberation-gnus is loaded and customized the key-bindings in the Viewer will be able to call into it, see Ticket Viewer.

Tracking Updates
****************

The functions in rt-liberation-update.el help keep up with updates to the ticket database. To enable the feature, `require' it after loading rt-liberation:

     (require 'rt-liberation-update)

Then set RT-LIBER-UPDATE-DEFAULT-QUEUE to be the name of the queue to watch for updates. For example:

     (setq rt-liber-update-default-queue "complaints")

 -- Function: rt-liber-update &optional no-update
     rt-liber-update is an interactive function which runs a query against the RT server asking for the tickets which have been updated since the time rt-liber-update was last run (each time it runs, it leaves a time-stamp). If no time-stamp is found, for instance when you run rt-liber-update for the first time, today's date is used.

     With the NO-UPDATE prefix, rt-liber-update will not update the time-stamp so that the next invocation will produce the same result.

Batch Operations
****************

The extension rt-liberation-multi.el implements performing batch operations on groups of tickets. It works in two stages: First mark an arbitrary number of tickets within the same buffer then call a batch operation function on them. The batch operation functions work the same way as function which work on single tickets only that they iterate through all of the marked tickets.

To enable batch operations first load rt-liberation-multi.el:

     (require 'rt-liberation-storage)

M
     Mark the ticket at point for future action. If the ticket at point is already marked then unmark it.

 -- Function: rt-liber-multi-set-status-open
     Set the status of all the marked tickets to ``open''.

 -- Function: rt-liber-multi-set-status-resolved
     Set the status of all the marked tickets to ``resolved.

 -- Function: rt-liber-multi-assign name
     Assign all of the marked tickets to NAME.

 -- Function: rt-liber-multi-flag-as-spam-and-delete
     Set the status of all the marked tickets to ``is-spam'' and delete.

Local Storage
*************

rt-liberation-storage.el implements associating arbitrary ancillary data with tickets. The data is stored locally and is not sent to the RT server.

To enable local storage first load rt-liberation-storage.el:

     (require 'rt-liberation-storage)

Then enable the display of ancillary data with:

     (setq rt-liber-anc-p t)

The associated data is edited and displayed in the ticket browser with the following command key:

A
     Associate text with the ticket at point. You will be prompted to enter a string of text.

Once text is associated with a ticket it will be displayed alongside that ticket in the ticket browser. This particular feature lends itself to creating private annotations about tickets.

The implementation distributed with rt-liberation allows associating text with tickets but is not limited to text. The same implementation can be extended to associate any arbitrary data with any ticket.

     [file gpl.texi not included]
     [file fdl.texi not included]