summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYoni Rabkin <yoni@rabkins.net>2020-12-16 12:01:12 -0500
committerYoni Rabkin <yoni@rabkins.net>2020-12-16 12:01:12 -0500
commitf682e7bfac617da558a5a342553e325150928b00 (patch)
treec238408f1885ab19dc84622b414651e7ad9cc008
parent6b062b7705fa7e23022d565a7ffe69494f3eff19 (diff)
parentd964a9295fc69b2bb5ad6662cc72a6f4570925d5 (diff)
Merge branch 'master' into externals/rt-liberation
-rw-r--r--Makefile2
-rw-r--r--NEWS9
-rw-r--r--doc/developer-release.txt39
-rw-r--r--doc/rt-liber.texinfo99
-rw-r--r--rt-liber.info209
-rw-r--r--rt-liberation-gnus.el12
-rw-r--r--rt-liberation-multi.el2
-rw-r--r--rt-liberation-report.el2
-rw-r--r--rt-liberation-rest.el40
-rw-r--r--rt-liberation-storage.el2
-rw-r--r--rt-liberation-update.el2
-rw-r--r--rt-liberation.el907
12 files changed, 876 insertions, 449 deletions
diff --git a/Makefile b/Makefile
index 8f3e500..9005179 100644
--- a/Makefile
+++ b/Makefile
@@ -34,3 +34,5 @@ all: $(TARGET)
clean:
-rm -f *~ *.elc
+
+neat: all clean
diff --git a/NEWS b/NEWS
index 9a75035..2490ea3 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,13 @@
-New in version 1.0:
+New in version ...
+
+ * Support getting REST credentials from auth-source.
+
+
+New in versions 1.0 to 1.31:
* Upgrade to GPLv3 (with the kind permission of the people from
Best Practical).
* Remove the CLI interface.
+
+ * Make sure the manual appears in ELPA.
diff --git a/doc/developer-release.txt b/doc/developer-release.txt
index e976f0b..e15282c 100644
--- a/doc/developer-release.txt
+++ b/doc/developer-release.txt
@@ -1,18 +1,41 @@
-*- outline -*-
-* Notes for preparing a release for rt-liberation
+This is an outline of how to make a release for rt-liberation via GNU
+ELPA.
+* compile
+Check for and correct compile-time errors and warnings.
-** Version
+* version
In order for the ELPA system to trigger a release the version number
in the comment header of rt-liberation.el must be incremented.
+* Good News
+Update the NEWS file to tell all of the people the Good News.
-** Info
-After each manual update the info file needs to be copied into the
-root of the project so that ELPA can pick it up.
+* documentation
+Update the manual, compile it, and update the copy of the info file in
+the root directory so that ELPA can install it.
+Compile an HTML version of the manual and update it on the Website:
-** ELPA
-Updates to the Savannah repository that have been tested can be pushed
-to the ELPA repository at externals/rt-liberation.
+ $ cvs commit -m "update manual" rt-liber.html
+
+ $ cvs commit -m "update website" index.html
+
+* push
+Push these updates to the git repo.
+
+* tag
+Tag the release with the ELPA version number:
+
+ $ git tag -a 2.00 -m "2.00"
+
+Then push that tag to the VCS:
+
+ $ git push --tags origin "2.00"
+
+* ELPA
+Push the changes to externals/rt-liberation on elpa.git with:
+
+ $ git push elpa elpa:refs/heads/externals/rt-liberation
diff --git a/doc/rt-liber.texinfo b/doc/rt-liber.texinfo
index b8cb1f5..bc2a095 100644
--- a/doc/rt-liber.texinfo
+++ b/doc/rt-liber.texinfo
@@ -48,7 +48,8 @@ This is the Manual for the rt-liberation system
@menu
* Introduction:: Introduction to rt-liberation.
-* Installation:: Setup rt-liberation to work on the system.
+* Installation:: Install rt-liberation on the system.
+* Configuration:: Setup rt-liberation to work on the system.
Using rt-liberation
* Queries:: Retrieve particular tickets from the server.
@@ -117,10 +118,12 @@ operations on the tickets.
@chapter Installation
@cindex installation
-rt-liberation is available via GNU ELPA.
+rt-liberation is available via GNU ELPA. Invoke @kbd{M-x
+list-packages} and choose to install the rt-liberation package.
-If you install rt-liberation manually instead you'll need to tell
-Emacs where to find it, and tell Emacs to load the package:
+If you install rt-liberation manually, by copying the code to your
+machine, instead you'll need to tell Emacs where to find it, and then
+tell Emacs to load the package:
@lisp
(add-to-list 'load-path "/PATH/TO/rt-liberation/")
@@ -131,6 +134,11 @@ Emacs where to find it, and tell Emacs to load the package:
@end lisp
+@c --------------------------------------------------
+@node Configuration
+@chapter Configuration
+@cindex configuration
+
rt-liberation needs to be configured in your ~/.emacs, an ~/.rt-liber
file, or similar.
@@ -140,6 +148,25 @@ Tell rt-liberation where to find the RT server's REST interface:
(setq rt-liber-rest-url "rt.example.org")
@end lisp
+In order to authenticate with the RT server instance you need to
+provide credentials. rt-liberation looks for these in the variables
+@var{rt-liber-rest-username} and @var{rt-liber-rest-password}. You can
+set these directly:
+
+@lisp
+(setq rt-liber-rest-username "someuser"
+ rt-liber-rest-password "somepassword")
+@end lisp
+
+You can also leave these values unset (@code{nil}), in which case
+rt-liberation will look for the credentials in a Netrc file via the
+auth-source library (see: @xref{Top,,, auth, Emacs auth-source}),
+under the machine name "rt-liberation":
+
+@example
+machine rt-liberation login someuser password somepassword
+@end example
+
rt-liberation can issue a command to ``take'' a ticket (that is,
assign it to yourself). For this the variable @var{rt-liber-username}
must be set:
@@ -148,9 +175,9 @@ must be set:
(setq rt-liber-username "someuser")
@end lisp
-rt-liberation can also launch a Web browser to visit a ticket. For
-that to work the base URL needs to be set in
-@var{rt-liber-base-url}. For example:
+rt-liberation can launch a Web browser to visit a ticket. For that to
+work the base URL needs to be set in @var{rt-liber-base-url}. For
+example:
(setq rt-liber-base-url "https://rt.foo.org/")
@@ -603,10 +630,8 @@ buffers will be created displaying the query results and named
@chapter Ticket Viewer
@cindex 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 @var{rt-liber-viewer-font-lock-keywords} and a number of
-key-bindings.
+The ticket viewer is an interface for viewing the contents of a ticket
+and for sending answers.
The ticket viewer provides key-bindings to help compose emails to send
to the RT email interface. The key-bindings for composing email
@@ -615,34 +640,26 @@ them depends on the email-backend system you have installed into
rt-liberation. @file{rt-liberation-gnus.el} provides integration with
Gnus, @xref{Gnus Integration}.
-Setting @var{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 @var{rt-liber-jump-to-latest} is set to
-`nil'.
-
-When in the ticket viewer buffer, invoking
-@dfn{rt-liber-viewer-take-ticket} will ``take'' the ticket.
-
@table @kbd
@item q
@kindex q (ticket viewer)
-@findex rt-liber-viewer-mode-quit
+@findex rt-liber-viewer2-mode-quit
Bury the ticket viewer buffer.
@item n
@kindex n (ticket viewer)
-@findex rt-liber-next-section-in-viewer
-Move point to the next section in ticket.
+@findex rt-liber-viewer2-next-section-in
+Move to the next section in ticket.
@item N
@kindex N (ticket viewer)
-@findex rt-liber-jump-to-latest-correspondence
-Move point to the newest correspondence section, if any.
+@findex rt-liber-viewer2-last-section-in
+Move to the last section.
@item p
@kindex p (ticket viewer)
-@findex rt-liber-previous-section-in-viewer
+@findex rt-liber-viewer2-previous-section-in
Move point to the previous section in ticket.
@item V
@@ -650,43 +667,15 @@ Move point to the previous section in ticket.
@findex rt-liber-viewer-visit-in-browser
Visit the current ticket in a Web browser.
-@item m
-@kindex m (ticket viewer)
-@findex rt-liber-viewer-answer
-Compose an answer to the current ticket.
-
@item M
@kindex M (ticket viewer)
-@findex rt-liber-viewer-answer-this
+@findex rt-liber-viewer2-answer
Compose an answer to the current ticket. The content section around
point will be inserted into the email body and commented out.
-@item t
-@kindex t (ticket viewer)
-@findex rt-liber-viewer-answer-provisionally
-Compose a provisional answer to the current ticket.
-
-@item T
-@kindex t (ticket viewer)
-@findex rt-liber-viewer-answer-provisionally-this
-Compose a provisional answer to the current ticket. The content
-section around point will be inserted into the email body and
-commented out.
-
-@item F
-@kindex F (ticket viewer)
-@findex rt-liber-viewer-answer-verbatim-this
-Compose an answer to the current ticket. The content section around
-point will be inserted into the email body verbatim.
-
-@item c
-@kindex c (ticket viewer)
-@findex rt-liber-viewer-comment
-Compose a comment for the current ticket.
-
@item C
@kindex C (ticket viewer)
-@findex rt-liber-viewer-comment-this
+@findex rt-liber-viewer2-comment
Comment on the ticket using the current context
@item g
diff --git a/rt-liber.info b/rt-liber.info
index f712b66..c2df1d8 100644
--- a/rt-liber.info
+++ b/rt-liber.info
@@ -36,7 +36,8 @@ Foundation, Inc.
* Menu:
* Introduction:: Introduction to rt-liberation.
-* Installation:: Setup rt-liberation to work on the system.
+* Installation:: Install rt-liberation on the system.
+* Configuration:: Setup rt-liberation to work on the system.
Using rt-liberation
* Queries:: Retrieve particular tickets from the server.
@@ -95,42 +96,64 @@ browsing the resulting tickets, viewing the tickets' contents and
performing operations on the tickets.

-File: rt-liber.info, Node: Installation, Next: Queries, Prev: Introduction, Up: Top
+File: rt-liber.info, Node: Installation, Next: Configuration, Prev: Introduction, Up: Top
2 Installation
**************
-rt-liberation is available via GNU ELPA.
+rt-liberation is available via GNU ELPA. Invoke 'M-x list-packages' and
+choose to install the rt-liberation package.
- If you install rt-liberation manually instead you'll need to tell
-Emacs where to find it, and tell Emacs to load the package:
+ If you install rt-liberation manually, by copying the code to your
+machine, instead you'll need to tell Emacs where to find it, and then
+tell Emacs to load the package:
(add-to-list 'load-path "/PATH/TO/rt-liberation/")
(require 'rt-liberation)
- rt-liberation needs to be configured in your ~/.emacs, an ~/.rt-liber
+
+File: rt-liber.info, Node: Configuration, Next: Queries, Prev: Installation, Up: Top
+
+3 Configuration
+***************
+
+rt-liberation needs to be configured in your ~/.emacs, an ~/.rt-liber
file, or similar.
Tell rt-liberation where to find the RT server's REST interface:
(setq rt-liber-rest-url "rt.example.org")
+ In order to authenticate with the RT server instance you need to
+provide credentials. rt-liberation looks for these in the variables
+RT-LIBER-REST-USERNAME and RT-LIBER-REST-PASSWORD. You can set these
+directly:
+
+ (setq rt-liber-rest-username "someuser"
+ rt-liber-rest-password "somepassword")
+
+ You can also leave these values unset ('nil'), in which case
+rt-liberation will look for the credentials in a Netrc file via the
+auth-source library (see: *Note (auth)Top::), under the machine name
+"rt-liberation":
+
+ machine rt-liberation login someuser password somepassword
+
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:
+ rt-liberation can 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/")

-File: rt-liber.info, Node: Queries, Next: Ticket Browser, Prev: Installation, Up: Top
+File: rt-liber.info, Node: Queries, Next: Ticket Browser, Prev: Configuration, Up: Top
-3 Queries
+4 Queries
*********
A typical RT server is meant to manage a large amount of tickets. Much
@@ -148,7 +171,7 @@ RT's TicketSQL language.

File: rt-liber.info, Node: Query Compiler, Next: Query Language, Up: Queries
-3.1 Query Compiler
+4.1 Query Compiler
==================
In order to browse and view tickets a list of needs to be requested from
@@ -164,7 +187,7 @@ number of TicketSQL tokens.

File: rt-liber.info, Node: Query Language, Prev: Query Compiler, Up: Queries
-3.2 Query Language
+4.2 Query Language
==================
rt-liberation's Sexp-based query language covers a portion of the
@@ -232,7 +255,7 @@ in function calls:

File: rt-liber.info, Node: Ticket Browser, Next: Ticket Viewer, Prev: Queries, Up: Top
-4 Ticket Browser
+5 Ticket Browser
****************
The ticket browser is a special buffer which provides a convenient
@@ -324,7 +347,7 @@ string, the following is equivalent:

File: rt-liber.info, Node: Ticket Browser Display, Next: Ticket Browser Sorting, Up: Ticket Browser
-4.1 Ticket Browser Display
+5.1 Ticket Browser Display
==========================
The ticket browser displays the tickets in the browser by calling
@@ -384,7 +407,7 @@ RT-LIBER-BROWSER-PRIORITY-CUTOFF

File: rt-liber.info, Node: Ticket Browser Sorting, Next: Ticket Browser Filtering, Prev: Ticket Browser Display, Up: Ticket Browser
-4.2 Ticket Browser Sorting
+5.2 Ticket Browser Sorting
==========================
The tickets in the browser are displayed by default in reverse
@@ -431,7 +454,7 @@ functions to perform comparisons between ticket objects:

File: rt-liber.info, Node: Ticket Browser Filtering, Next: Multiple Ticket Browsers, Prev: Ticket Browser Sorting, Up: Ticket Browser
-4.3 Ticket Browser Filtering
+5.3 Ticket Browser Filtering
============================
The Ticket Browser can also filter out (that is, not display) certain
@@ -469,7 +492,7 @@ ticket is to be filtered.

File: rt-liber.info, Node: Multiple Ticket Browsers, Prev: Ticket Browser Filtering, Up: Ticket Browser
-4.4 Multiple Ticket Browsers
+5.4 Multiple Ticket Browsers
============================
It is sometimes useful to rename the ticket browser buffer to something
@@ -503,12 +526,11 @@ supervisor*" and "*new tickets*" respectively:

File: rt-liber.info, Node: Ticket Viewer, Next: Gnus Integration, Prev: Ticket Browser, Up: Top
-5 Ticket Viewer
+6 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 is an interface for viewing the contents of a ticket
+and for sending answers.
The ticket viewer provides key-bindings to help compose emails to
send to the RT email interface. The key-bindings for composing email
@@ -517,21 +539,14 @@ depends on the email-backend system you have installed into
rt-liberation. 'rt-liberation-gnus.el' provides integration with Gnus,
*Note 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.
+ Move to the next section in ticket.
'N'
- Move point to the newest correspondence section, if any.
+ Move to the last section.
'p'
Move point to the previous section in ticket.
@@ -539,29 +554,11 @@ is visited. By default RT-LIBER-JUMP-TO-LATEST is set to 'nil'.
'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
@@ -580,7 +577,7 @@ is visited. By default RT-LIBER-JUMP-TO-LATEST is set to 'nil'.

File: rt-liber.info, Node: Gnus Integration, Next: Tracking Updates, Prev: Ticket Viewer, Up: Top
-6 Gnus Integration
+7 Gnus Integration
******************
The file 'rt-liberation-gnus.el' implements integration with Gnus for
@@ -635,7 +632,7 @@ the Viewer will be able to call into it, *Note Ticket Viewer::.

File: rt-liber.info, Node: Tracking Updates, Next: Batch Operations, Prev: Gnus Integration, Up: Top
-7 Tracking Updates
+8 Tracking Updates
******************
The functions in 'rt-liberation-update.el' help keep up with updates to
@@ -664,7 +661,7 @@ watch for updates. For example:

File: rt-liber.info, Node: Batch Operations, Next: Local Storage, Prev: Tracking Updates, Up: Top
-8 Batch Operations
+9 Batch Operations
******************
The extension 'rt-liberation-multi.el' implements performing batch
@@ -697,8 +694,8 @@ through all of the marked tickets.

File: rt-liber.info, Node: Local Storage, Next: Copying, Prev: Batch Operations, Up: Top
-9 Local Storage
-***************
+10 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
@@ -730,7 +727,7 @@ can be extended to associate any arbitrary data with any ticket.

File: rt-liber.info, Node: Copying, Next: The GNU FDL, Prev: Local Storage, Up: Top
-10 The GNU General Public License.
+11 The GNU General Public License.
**********************************
Version 3, 29 June 2007
@@ -1446,7 +1443,7 @@ please read <https://www.gnu.org/licenses/why-not-lgpl.html>.

File: rt-liber.info, Node: The GNU FDL, Next: Concept Index, Prev: Copying, Up: Top
-11 GNU Free Documentation License
+12 GNU Free Documentation License
*********************************
Version 1.2, November 2002
@@ -1841,7 +1838,7 @@ File: rt-liber.info, Node: The GNU FDL, Next: Concept Index, Prev: Copying,
choose any version ever published (not as a draft) by the Free
Software Foundation.
-11.1 ADDENDUM: How to use this License for your documents
+12.1 ADDENDUM: How to use this License for your documents
=========================================================
To use this License in a document you have written, include a copy of
@@ -1882,6 +1879,7 @@ Concept Index
* Menu:
* Batch Operations: Batch Operations. (line 6)
+* configuration: Configuration. (line 6)
* FDL, GNU Free Documentation License: The GNU FDL. (line 6)
* Gnus Integration: Gnus Integration. (line 6)
* installation: Installation. (line 6)
@@ -1912,7 +1910,7 @@ Function Index
* Menu:
* revert-buffer: Ticket Browser. (line 48)
-* revert-buffer <1>: Ticket Viewer. (line 66)
+* revert-buffer <1>: Ticket Viewer. (line 40)
* rt-liber-browse-query: Ticket Browser. (line 12)
* rt-liber-browser-ancillary-text: Local Storage. (line 22)
* rt-liber-browser-assign: Ticket Browser. (line 62)
@@ -1925,7 +1923,6 @@ Function Index
* rt-liber-browser-resolve: Ticket Browser. (line 65)
* rt-liber-browser-take-ticket-at-point: Ticket Browser. (line 71)
* rt-liber-display-ticket-at-point: Ticket Browser. (line 45)
-* rt-liber-jump-to-latest-correspondence: Ticket Viewer. (line 31)
* rt-liber-lex-lessthan-p: Ticket Browser Sorting.
(line 15)
* rt-liber-mark-ticket-at-point: Batch Operations. (line 18)
@@ -1934,27 +1931,23 @@ Function Index
* rt-liber-multi-flag-as-spam-and-delete: Batch Operations. (line 30)
* rt-liber-multi-set-status-open: Batch Operations. (line 21)
* rt-liber-multi-set-status-resolved: Batch Operations. (line 24)
-* rt-liber-next-section-in-viewer: Ticket Viewer. (line 28)
* rt-liber-next-ticket-in-browser: Ticket Browser. (line 39)
-* rt-liber-previous-section-in-viewer: Ticket Viewer. (line 34)
* rt-liber-previous-ticket-in-browser: Ticket Browser. (line 42)
* rt-liber-time-lessthan-p: Ticket Browser Sorting.
(line 30)
* rt-liber-update: Tracking Updates. (line 17)
-* rt-liber-viewer-answer: Ticket Viewer. (line 40)
-* rt-liber-viewer-answer-provisionally: Ticket Viewer. (line 48)
-* rt-liber-viewer-answer-provisionally-this: Ticket Viewer. (line 51)
-* rt-liber-viewer-answer-this: Ticket Viewer. (line 43)
-* rt-liber-viewer-answer-verbatim-this: Ticket Viewer. (line 56)
-* rt-liber-viewer-comment: Ticket Viewer. (line 60)
-* rt-liber-viewer-comment-this: Ticket Viewer. (line 63)
-* rt-liber-viewer-mode-quit: Ticket Viewer. (line 25)
-* rt-liber-viewer-show-ticket-browser: Ticket Viewer. (line 75)
-* rt-liber-viewer-visit-in-browser: Ticket Viewer. (line 37)
+* rt-liber-viewer-show-ticket-browser: Ticket Viewer. (line 49)
+* rt-liber-viewer-visit-in-browser: Ticket Viewer. (line 29)
+* rt-liber-viewer2-answer: Ticket Viewer. (line 32)
+* rt-liber-viewer2-comment: Ticket Viewer. (line 37)
+* rt-liber-viewer2-last-section-in: Ticket Viewer. (line 23)
+* rt-liber-viewer2-mode-quit: Ticket Viewer. (line 17)
+* rt-liber-viewer2-next-section-in: Ticket Viewer. (line 20)
+* rt-liber-viewer2-previous-section-in: Ticket Viewer. (line 26)
* scroll-down: Ticket Browser. (line 77)
-* scroll-down <1>: Ticket Viewer. (line 72)
+* scroll-down <1>: Ticket Viewer. (line 46)
* scroll-up: Ticket Browser. (line 74)
-* scroll-up <1>: Ticket Viewer. (line 69)
+* scroll-up <1>: Ticket Viewer. (line 43)

File: rt-liber.info, Node: Variable Index, Next: Keybinding Index, Prev: Function Index, Up: Top
@@ -1980,63 +1973,59 @@ Keybinding Index
* a (ticket browser): Ticket Browser. (line 62)
* A (ticket browser): Local Storage. (line 22)
-* c (ticket viewer): Ticket Viewer. (line 60)
-* C (ticket viewer): Ticket Viewer. (line 63)
+* C (ticket viewer): Ticket Viewer. (line 37)
* DEL (ticket browser): Ticket Browser. (line 77)
-* DEL (ticket viewer): Ticket Viewer. (line 72)
-* F (ticket viewer): Ticket Viewer. (line 56)
+* DEL (ticket viewer): Ticket Viewer. (line 46)
* g (ticket browser): Ticket Browser. (line 48)
* G (ticket browser): Ticket Browser. (line 51)
-* g (ticket viewer): Ticket Viewer. (line 66)
-* h (ticket viewer): Ticket Viewer. (line 75)
+* g (ticket viewer): Ticket Viewer. (line 40)
+* h (ticket viewer): Ticket Viewer. (line 49)
* m (ticket browser): Ticket Browser. (line 80)
* M (ticket browser): Batch Operations. (line 18)
-* m (ticket viewer): Ticket Viewer. (line 40)
-* M (ticket viewer): Ticket Viewer. (line 43)
+* M (ticket viewer): Ticket Viewer. (line 32)
* n (ticket browser): Ticket Browser. (line 39)
-* n (ticket viewer): Ticket Viewer. (line 28)
-* N (ticket viewer): Ticket Viewer. (line 31)
+* n (ticket viewer): Ticket Viewer. (line 20)
+* N (ticket viewer): Ticket Viewer. (line 23)
* o (ticket browser): Ticket Browser. (line 68)
* p (ticket browser): Ticket Browser. (line 42)
* P (ticket browser): Ticket Browser. (line 83)
-* p (ticket viewer): Ticket Viewer. (line 34)
+* p (ticket viewer): Ticket Viewer. (line 26)
* q (ticket browser): Ticket Browser. (line 36)
-* q (ticket viewer): Ticket Viewer. (line 25)
+* q (ticket viewer): Ticket Viewer. (line 17)
* r (ticket browser): Ticket Browser. (line 65)
* RET (ticket browser): Ticket Browser. (line 45)
* s (ticket browser): Ticket Browser. (line 55)
* S (ticket browser): Ticket Browser. (line 58)
* SPC (ticket browser): Ticket Browser. (line 74)
-* SPC (ticket viewer): Ticket Viewer. (line 69)
+* SPC (ticket viewer): Ticket Viewer. (line 43)
* t (ticket browser): Ticket Browser. (line 71)
-* t (ticket viewer): Ticket Viewer. (line 48)
-* t (ticket viewer) <1>: Ticket Viewer. (line 51)
-* V (ticket viewer): Ticket Viewer. (line 37)
+* V (ticket viewer): Ticket Viewer. (line 29)

Tag Table:
Node: Top680
-Node: Introduction2956
-Node: Installation3563
-Node: Queries4542
-Node: Query Compiler5104
-Node: Query Language5745
-Node: Ticket Browser8184
-Node: Ticket Browser Display10656
-Node: Ticket Browser Sorting12647
-Node: Ticket Browser Filtering14597
-Node: Multiple Ticket Browsers16140
-Node: Ticket Viewer17309
-Node: Gnus Integration19481
-Node: Tracking Updates21769
-Node: Batch Operations22846
-Node: Local Storage24026
-Node: Copying25104
-Node: The GNU FDL62653
-Node: Concept Index85045
-Node: Function Index86626
-Node: Variable Index89976
-Node: Keybinding Index90352
+Node: Introduction3025
+Node: Installation3632
+Node: Configuration4139
+Node: Queries5451
+Node: Query Compiler6014
+Node: Query Language6655
+Node: Ticket Browser9094
+Node: Ticket Browser Display11566
+Node: Ticket Browser Sorting13557
+Node: Ticket Browser Filtering15507
+Node: Multiple Ticket Browsers17050
+Node: Ticket Viewer18219
+Node: Gnus Integration19478
+Node: Tracking Updates21766
+Node: Batch Operations22843
+Node: Local Storage24023
+Node: Copying25103
+Node: The GNU FDL62652
+Node: Concept Index85044
+Node: Function Index86698
+Node: Variable Index89683
+Node: Keybinding Index90059

End Tag Table
diff --git a/rt-liberation-gnus.el b/rt-liberation-gnus.el
index eb0b262..789e482 100644
--- a/rt-liberation-gnus.el
+++ b/rt-liberation-gnus.el
@@ -1,4 +1,4 @@
-;;; rt-liberation-gnus.el --- Gnus integration for rt-liberation
+;;; rt-liberation-gnus.el --- Gnus integration for rt-liberation -*- lexical-binding: t; -*-
;; Copyright (C) 2009-2014 Free Software Foundation, Inc.
;;
@@ -32,16 +32,6 @@
:prefix "rt-liber-gnus-"
:group 'rt-liber-gnus)
-(defcustom rt-liber-gnus-comment-address "no comment address set"
- "*Email address for adding a comment."
- :type 'string
- :group 'rt-liber-gnus)
-
-(defcustom rt-liber-gnus-address "no reply address set"
- "*Email address for replying to requestor."
- :type 'string
- :group 'rt-liber-gnus)
-
(defcustom rt-liber-gnus-subject-name "no subject name set"
"*Subject name to be included in email header."
:type 'string
diff --git a/rt-liberation-multi.el b/rt-liberation-multi.el
index 8e4665a..d4a8602 100644
--- a/rt-liberation-multi.el
+++ b/rt-liberation-multi.el
@@ -1,4 +1,4 @@
-;;; rt-liberation-multi.el --- Emacs interface to RT
+;;; rt-liberation-multi.el --- Emacs interface to RT -*- lexical-binding: t; -*-
;; Copyright (C) 2010, 2014 Free Software Foundation, Inc.
;;
diff --git a/rt-liberation-report.el b/rt-liberation-report.el
index 11d20c2..a60ae5b 100644
--- a/rt-liberation-report.el
+++ b/rt-liberation-report.el
@@ -1,4 +1,4 @@
-;;; rt-liberation-report.el --- Emacs interface to RT
+;;; rt-liberation-report.el --- Emacs interface to RT -*- lexical-binding: t; -*-
;; Copyright (C) 2015 Free Software Foundation, Inc.
;;
diff --git a/rt-liberation-rest.el b/rt-liberation-rest.el
index 99f6ce9..a7eb076 100644
--- a/rt-liberation-rest.el
+++ b/rt-liberation-rest.el
@@ -1,4 +1,4 @@
-;;; rt-liberation-rest.el --- Interface to the RT REST API
+;;; rt-liberation-rest.el --- Interface to the RT REST API -*- lexical-binding: t; -*-
;; Copyright (C) 2014-2015 Free Software Foundation, Inc.
;;
@@ -31,8 +31,12 @@
(require 'url)
(require 'url-util)
+(require 'auth-source)
+;;; ------------------------------------------------------------------
+;;; variables and constants
+;;; ------------------------------------------------------------------
(defvar rt-liber-rest-debug-buffer-name "*rt-liber-rest debug log*"
"Buffer name of debug capture.")
@@ -45,16 +49,19 @@
(defvar rt-liber-rest-url ""
"URL of RT installation.")
-(defvar rt-liber-rest-username ""
+(defvar rt-liber-rest-username nil
"Username of RT account.")
-(defvar rt-liber-rest-password ""
+(defvar rt-liber-rest-password nil
"Password of RT account.")
(defvar rt-liber-rest-verbose-p t
"If non-nil, be verbose about what's happening.")
+;;; ------------------------------------------------------------------
+;;; functions
+;;; ------------------------------------------------------------------
(defun rt-liber-rest-write-debug (str)
"Write to debug buffer."
(when (not (stringp str))
@@ -65,6 +72,21 @@
(goto-char (point-max))
(insert str))))
+(defun rt-liber-rest-auth ()
+ "Try to get the REST credentials."
+ (if (and (stringp rt-liber-rest-username)
+ (stringp rt-liber-rest-password)
+ (< 0 (length rt-liber-rest-username))
+ (< 0 (length rt-liber-rest-password)))
+ t
+ (message "rt-liber: no REST credentials set, so attempting auth-source")
+ (let ((auth-source-found-p
+ (auth-source-search :host "rt-liberation" :require '(:user :secret) :create nil)))
+ (when (not auth-source-found-p)
+ (error "no auth-source found for login"))
+ (setq rt-liber-rest-password (funcall (plist-get (nth 0 auth-source-found-p) :secret))
+ rt-liber-rest-username (plist-get (nth 0 auth-source-found-p) :user)))))
+
(defun rt-liber-rest-search-string (scheme url username password query)
"Return the search query string."
(let ((user (url-encode-url username))
@@ -124,11 +146,11 @@
str)
(setq str
(decode-coding-string
- (with-current-buffer response
- (buffer-substring-no-properties (point-min)
- (point-max)))
- 'utf-8))
-
+ (with-current-buffer response
+ (buffer-substring-no-properties (point-min)
+ (point-max)))
+ 'utf-8))
+ (message "done")
(rt-liber-rest-write-debug
(format "outgoing rest call -->\n%s\n<-- incoming\n%s\n" url str))
str)))
@@ -138,6 +160,7 @@
(when (or (not (stringp op))
(not (stringp query-string)))
(error "bad arguments"))
+ (rt-liber-rest-auth)
(cond ((string= op "ls")
(rt-liber-rest-call
(rt-liber-rest-search-string rt-liber-rest-scheme
@@ -215,6 +238,7 @@
"Run edit comment to set FIELD to VALUE."
(message "started edit command at %s..." (current-time-string))
(message "ticket #%s, %s <- %s" ticket-id field value)
+ (rt-liber-rest-auth)
(let ((request-data
(format "content=%s: %s"
(url-hexify-string field)
diff --git a/rt-liberation-storage.el b/rt-liberation-storage.el
index df82ce1..f8e6884 100644
--- a/rt-liberation-storage.el
+++ b/rt-liberation-storage.el
@@ -1,4 +1,4 @@
-;;; rt-liberation-storage.el --- Storage backend for rt-liberation
+;;; rt-liberation-storage.el --- Storage backend for rt-liberation -*- lexical-binding: t; -*-
;; Copyright (C) 2010 Free Software Foundation, Inc.
;;
diff --git a/rt-liberation-update.el b/rt-liberation-update.el
index bdd0f75..17ddba3 100644
--- a/rt-liberation-update.el
+++ b/rt-liberation-update.el
@@ -1,4 +1,4 @@
-;;; rt-liberation-update.el --- check updated tickets
+;;; rt-liberation-update.el --- check updated tickets -*- lexical-binding: t; -*-
;; Copyright (C) 2009 Free Software Foundation, Inc.
;;
diff --git a/rt-liberation.el b/rt-liberation.el
index f30e048..4ee51c9 100644
--- a/rt-liberation.el
+++ b/rt-liberation.el
@@ -1,11 +1,11 @@
-;;; rt-liberation.el --- Emacs interface to RT
+;;; rt-liberation.el --- Emacs interface to RT -*- lexical-binding: t; -*-
;; Copyright (C) 2008-2020 Free Software Foundation, Inc.
;; Author: Yoni Rabkin <yrk@gnu.org>
;; Authors: Aaron S. Hawley <aaron.s.hawley@gmail.com>, John Sullivan <johnsu01@wjsullivan.net>
;; Maintainer: Yoni Rabkin <yrk@gnu.org>
-;; Version: 1.31
+;; Version: 2.01
;; Keywords: rt, tickets
;; Package-Type: multi
;; url: http://www.nongnu.org/rtliber/
@@ -38,13 +38,16 @@
;;; Code:
-
(require 'browse-url)
(require 'time-date)
(require 'cl-lib)
(require 'rt-liberation-rest)
+(declare-function rt-liber-get-ancillary-text "rt-liberation-storage.el")
+(declare-function rt-liber-ticket-marked-p "rt-liberation-multi.el")
+(declare-function rt-liber-set-ancillary-text "rt-liberation-storage.el")
+
(defgroup rt-liber nil
"*rt-liberation, the Emacs interface to RT"
@@ -56,6 +59,24 @@
:type 'string
:group 'rt-liber)
+(defvar rt-liber-viewer-section-header-regexp
+ "^# [0-9]+/[0-9]+ (id/[0-9]+/total)")
+
+(defvar rt-liber-viewer-section-field-regexp
+ "^\\(.+\\): \\(.+\\)$")
+
+(defconst rt-liber-viewer-font-lock-keywords
+ (let ((header-regexp (regexp-opt '("id: " "Ticket: " "TimeTaken: "
+ "Type: " "Field: " "OldValue: "
+ "NewValue: " "Data: "
+ "Description: " "Created: "
+ "Creator: " "Attachments: ")
+ t)))
+ (list
+ (list (concat "^" header-regexp ".*$") 0
+ 'font-lock-comment-face)))
+ "Expressions to font-lock for RT ticket viewer.")
+
(defvar rt-liber-created-string "Created"
"String representation of \"created\" query tag.")
@@ -112,7 +133,6 @@
(defvar rt-liber-browser-default-filter-function
'rt-liber-default-filter-f
"Default filtering function.
-
This is a function which accepts the ticket alist as a single
argument and returns nil if the ticket needs to be filtered out,
dropped or ignored (however you wish to put it.), otherwise the
@@ -163,17 +183,6 @@ function returns a truth value.")
(t (:background "Black")))
"Face for high priority tickets in browser buffer.")
-(defconst rt-liber-viewer-font-lock-keywords
- (let ((header-regexp (regexp-opt '("id: " "Ticket: " "TimeTaken: "
- "Type: " "Field: " "OldValue: "
- "NewValue: " "Data: "
- "Description: " "Created: "
- "Creator: " "Attachments: ") t)))
- (list
- (list (concat "^" header-regexp ".*$") 0
- 'font-lock-comment-face)))
- "Expressions to font-lock for RT ticket viewer.")
-
(defvar rt-liber-browser-do-refresh t
"When t, run `rt-liber-browser-refresh' otherwise disable it.")
@@ -193,7 +202,6 @@ server.")
(status . "Status")
(priority . "Priority"))
"Mapping between field symbols and RT field strings.
-
The field symbols provide the programmer with a consistent way of
referring to RT fields.")
@@ -203,14 +211,12 @@ referring to RT fields.")
(open . "open")
(new . "new"))
"Mapping between status symbols and status strings.
-
The status symbols provide the programmer with a consistent way
of referring to certain statuses. The status strings are the
server specific strings.")
(defvar rt-liber-debug-log-enable nil
"If t then enable logging of communication to a buffer.
-
Careful! This might create a sizable buffer.")
(defvar rt-liber-debug-log-buffer-name "*rt-liber debug log*"
@@ -218,19 +224,35 @@ Careful! This might create a sizable buffer.")
(defvar rt-liber-ticket-local nil
"Buffer local storage for a ticket.
-
This variable is made buffer local for the ticket history")
(defvar rt-liber-assoc-browser nil
"Browser associated with a ticket history.
-
This variable is made buffer local for the ticket history")
+(defcustom rt-liber-gnus-comment-address "no comment address set"
+ "*Email address for adding a comment."
+ :type 'string
+ :group 'rt-liber-gnus)
+
+(defcustom rt-liber-gnus-address "no reply address set"
+ "*Email address for replying to requestor."
+ :type 'string
+ :group 'rt-liber-gnus)
+
+(defvar rt-liber-display-ticket-at-point-f 'rt-liber-viewer2-display-ticket-at-point
+ "Function for displaying ticket at point in the browser.")
+
+(defvar rt-liber-viewer2-section-regexp "^Ticket [0-9]+ by "
+ "Regular expression to match a section in the viewer.")
+
+(defvar rt-liber-viewer2-recenter 4
+ "Argument passed to `recenter' in the viewer.")
+
;;; --------------------------------------------------------
;;; Debug log
;;; --------------------------------------------------------
-
(defun rt-liber-debug-log-write (str)
"Write STR to debug log."
(when (not (stringp str))
@@ -244,7 +266,6 @@ This variable is made buffer local for the ticket history")
;;; --------------------------------------------------------
;;; TicketSQL compiler
;;; --------------------------------------------------------
-
(defun rt-liber-bool-p (sym)
"Return t if SYM is a boolean operator, otherwise nil."
(member sym '(and or)))
@@ -352,7 +373,6 @@ AFTER date after predicate."
;;; --------------------------------------------------------
;;; Parse Answer
;;; --------------------------------------------------------
-
(defun rt-liber-parse-answer (answer-string parser-f)
"Operate on ANSWER-STRING with PARSER-F."
(with-temp-buffer
@@ -367,7 +387,6 @@ AFTER date after predicate."
;;; --------------------------------------------------------
;;; Ticket list retriever
;;; --------------------------------------------------------
-
(put 'rt-liber-no-result-from-query-error
'error-conditions
'(error rt-liber-errors rt-liber-no-result-from-query-error))
@@ -435,7 +454,6 @@ AFTER date after predicate."
;;; --------------------------------------------------------
;;; Ticket utilities
;;; --------------------------------------------------------
-
(defun rt-liber-ticket-days-old (ticket-alist)
"Return the age of the ticket in positive days."
(days-between (format-time-string "%Y-%m-%dT%T%z" (current-time))
@@ -445,26 +463,6 @@ AFTER date after predicate."
(<= rt-liber-ticket-old-threshold
(rt-liber-ticket-days-old ticket-alist)))
-
-;;; --------------------------------------------------------
-;;; Ticket viewer
-;;; --------------------------------------------------------
-
-(defun rt-liber-jump-to-latest-correspondence ()
- "Move point to the newest correspondence section."
- (interactive)
- (let (latest-point)
- (save-excursion
- (goto-char (point-max))
- (when (re-search-backward rt-liber-correspondence-regexp
- (point-min) t)
- (setq latest-point (point))))
- (if latest-point
- (progn
- (goto-char latest-point)
- (rt-liber-next-section-in-viewer))
- (message "no correspondence found"))))
-
(defun rt-liber-ticket-id-only (ticket-alist)
"Return numerical portion of ticket number from TICKET-ALIST."
(if ticket-alist
@@ -480,11 +478,6 @@ AFTER date after predicate."
nil))
nil))
-(defun rt-liber-get-field-string (field-symbol)
- (when (not field-symbol)
- (error "null field symbol"))
- (cdr (assoc field-symbol rt-liber-field-dictionary)))
-
(defun rt-liber-ticket-owner-only (ticket-alist)
"Return the string value of the ticket owner."
(when (not ticket-alist)
@@ -492,202 +485,15 @@ AFTER date after predicate."
(cdr (assoc (rt-liber-get-field-string 'owner)
ticket-alist)))
-(defun rt-liber-viewer-visit-in-browser ()
- "Visit this ticket in the RT Web interface."
- (interactive)
- (let ((id (rt-liber-ticket-id-only rt-liber-ticket-local)))
- (if id
- (browse-url
- (concat rt-liber-base-url "Ticket/Display.html?id=" id))
- (error "no ticket currently in view"))))
-
-(defun rt-liber-viewer-mode-quit ()
- "Bury the ticket viewer."
- (interactive)
- (bury-buffer))
-
-(defun rt-liber-viewer-show-ticket-browser ()
- "Return to the ticket browser buffer."
- (interactive)
- (let ((id (rt-liber-ticket-id-only rt-liber-ticket-local)))
- (if id
- (let ((target-buffer
- (if rt-liber-assoc-browser
- (buffer-name rt-liber-assoc-browser)
- (buffer-name rt-liber-browser-buffer-name))))
- (if target-buffer
- (switch-to-buffer target-buffer)
- (error "associated ticket browser buffer no longer exists"))
- (rt-liber-browser-move-point-to-ticket id))
- (error "no ticket currently in view"))))
-
-(defun rt-liber-next-section-in-viewer ()
- "Move point to next section."
- (interactive)
- (forward-line 1)
- (when (not (re-search-forward rt-liber-content-regexp (point-max) t))
- (message "no next section"))
- (goto-char (point-at-bol)))
-
-(defun rt-liber-previous-section-in-viewer ()
- "Move point to previous section."
- (interactive)
- (forward-line -1)
- (when (not (re-search-backward rt-liber-content-regexp (point-min) t))
- (message "no previous section"))
- (goto-char (point-at-bol)))
-
-(defconst rt-liber-viewer-mode-map
- (let ((map (make-sparse-keymap)))
- (define-key map (kbd "q") 'rt-liber-viewer-mode-quit)
- (define-key map (kbd "n") 'rt-liber-next-section-in-viewer)
- (define-key map (kbd "N") 'rt-liber-jump-to-latest-correspondence)
- (define-key map (kbd "p") 'rt-liber-previous-section-in-viewer)
- (define-key map (kbd "V") 'rt-liber-viewer-visit-in-browser)
- (define-key map (kbd "m") 'rt-liber-viewer-answer)
- (define-key map (kbd "M") 'rt-liber-viewer-answer-this)
- (define-key map (kbd "t") 'rt-liber-viewer-answer-provisionally)
- (define-key map (kbd "T") 'rt-liber-viewer-answer-provisionally-this)
- (define-key map (kbd "F") 'rt-liber-viewer-answer-verbatim-this)
- (define-key map (kbd "c") 'rt-liber-viewer-comment)
- (define-key map (kbd "C") 'rt-liber-viewer-comment-this)
- (define-key map (kbd "g") 'revert-buffer)
- (define-key map (kbd "SPC") 'scroll-up)
- (define-key map (kbd "DEL") 'scroll-down)
- (define-key map (kbd "h") 'rt-liber-viewer-show-ticket-browser)
- map)
- "Key map for ticket viewer.")
-
-(define-derived-mode rt-liber-viewer-mode nil
- "RT Liberation Viewer"
- "Major Mode for viewing RT tickets.
-\\{rt-liber-viewer-mode-map}"
- (set
- (make-local-variable 'font-lock-defaults)
- '((rt-liber-viewer-font-lock-keywords)))
- (set (make-local-variable 'revert-buffer-function)
- #'rt-liber-refresh-ticket-history)
- (set (make-local-variable 'buffer-stale-function)
- (lambda (&optional _noconfirm) 'slow))
- (when rt-liber-jump-to-latest
- (rt-liber-jump-to-latest-correspondence))
- (run-hooks 'rt-liber-viewer-hook))
-
-(defun rt-liber-display-ticket-history (ticket-alist &optional assoc-browser)
- "Display history for ticket.
-
-TICKET-ALIST alist of ticket data.
-ASSOC-BROWSER if non-nil should be a ticket browser."
- (let* ((ticket-id (rt-liber-ticket-id-only ticket-alist))
- (contents (rt-liber-rest-run-ticket-history-base-query ticket-id))
- (new-ticket-buffer (get-buffer-create
- (concat "*RT Ticket #" ticket-id "*"))))
- (with-current-buffer new-ticket-buffer
- (let ((inhibit-read-only t))
- (erase-buffer)
- (insert contents)
- (goto-char (point-min))
- (rt-liber-viewer-mode)
- (set
- (make-local-variable 'rt-liber-ticket-local)
- ticket-alist)
- (when assoc-browser
- (set
- (make-local-variable 'rt-liber-assoc-browser)
- assoc-browser))
- (set-buffer-modified-p nil)
- (setq buffer-read-only t)))
- (switch-to-buffer new-ticket-buffer)))
-
-(defun rt-liber-refresh-ticket-history (&optional _ignore-auto _noconfirm)
- (interactive)
- (if rt-liber-ticket-local
- (rt-liber-display-ticket-history rt-liber-ticket-local
- rt-liber-assoc-browser)
- (error "not viewing a ticket")))
-
-;; wrapper functions around specific functions provided by a backend
-
-(declare-function
- rt-liber-gnus-compose-reply-to-requestor
- "rt-liberation-gnus.el")
-(declare-function
- rt-liber-gnus-compose-reply-to-requestor-to-this
- "rt-liberation-gnus.el")
-(declare-function
- rt-liber-gnus-compose-reply-to-requestor-verbatim-this
- "rt-liberation-gnus.el")
-(declare-function
- rt-liber-gnus-compose-provisional
- "rt-liberation-gnus.el")
-(declare-function
- rt-liber-gnus-compose-provisional-to-this
- "rt-liberation-gnus.el")
-(declare-function
- rt-liber-gnus-compose-comment
- "rt-liberation-gnus.el")
-(declare-function
- rt-liber-gnus-compose-comment-this
- "rt-liberation-gnus.el")
-
-(defun rt-liber-viewer-answer ()
- "Answer the ticket."
- (interactive)
- (cond ((featurep 'rt-liberation-gnus)
- (rt-liber-gnus-compose-reply-to-requestor))
- (t (error "no function defined"))))
-
-(defun rt-liber-viewer-answer-this ()
- "Answer the ticket using the current context."
- (interactive)
- (cond ((featurep 'rt-liberation-gnus)
- (rt-liber-gnus-compose-reply-to-requestor-to-this))
- (t (error "no function defined"))))
-
-(defun rt-liber-viewer-answer-verbatim-this ()
- "Answer the ticket using the current context verbatim."
- (interactive)
- (cond ((featurep 'rt-liberation-gnus)
- (rt-liber-gnus-compose-reply-to-requestor-verbatim-this))
- (t (error "no function defined"))))
-
-(defun rt-liber-viewer-answer-provisionally ()
- "Provisionally answer the ticket."
- (interactive)
- (cond ((featurep 'rt-liberation-gnus)
- (rt-liber-gnus-compose-provisional))
- (t (error "no function defined"))))
-
-(defun rt-liber-viewer-answer-provisionally-this ()
- "Provisionally answer the ticket using the current context."
- (interactive)
- (cond ((featurep 'rt-liberation-gnus)
- (rt-liber-gnus-compose-provisional-to-this))
- (t (error "no function defined"))))
-
-(defun rt-liber-viewer-comment ()
- "Comment on the ticket."
- (interactive)
- (cond ((featurep 'rt-liberation-gnus)
- (rt-liber-gnus-compose-comment))
- (t (error "no function defined"))))
-
-(defun rt-liber-viewer-comment-this ()
- "Comment on the ticket using the current context."
- (interactive)
- (cond ((featurep 'rt-liberation-gnus)
- (rt-liber-gnus-compose-comment-this))
- (t (error "no function defined"))))
+(defun rt-liber-get-field-string (field-symbol)
+ (when (not field-symbol)
+ (error "null field symbol"))
+ (cdr (assoc field-symbol rt-liber-field-dictionary)))
;;; --------------------------------------------------------
;;; Ticket browser
;;; --------------------------------------------------------
-
-(declare-function
- rt-liber-get-ancillary-text
- "rt-liberation-storage.el")
-
;; accept a ticket-alist object and return an alist mapping ticket
;; properties to format characters for use in `rt-liber-format'.
(defun rt-liber-format-function (ticket-alist)
@@ -768,9 +574,6 @@ The ticket's priority is compared to the variable
'(face font-lock-comment-face)))
(newline))
-(declare-function rt-liber-ticket-marked-p
- "rt-liberation-multi.el")
-
(defun rt-liber-ticketlist-browser-redraw (ticketlist &optional query)
"Display TICKETLIST. Optionally display QUERY as well."
(erase-buffer)
@@ -861,6 +664,11 @@ If POINT is nil then called on (point)."
(let ((ticket-alist (get-text-property (point) 'rt-ticket)))
(rt-liber-display-ticket-history ticket-alist (current-buffer))))
+(defun rt-liber-ticket-at-point ()
+ "Display the contents of the ticket at point."
+ (interactive)
+ (funcall rt-liber-display-ticket-at-point-f))
+
(defun rt-liber-browser-search (id)
"Return point where ticket with ID is displayed or nil."
(let ((p nil))
@@ -895,7 +703,6 @@ If POINT is nil then called on (point)."
;;; --------------------------------------------------------
;;; Ticket browser sorting
;;; --------------------------------------------------------
-
(defun rt-liber-lex-lessthan-p (a b field)
"Return t if A is lexicographically less than B in FIELD."
(let ((field-a (cdr (assoc field a)))
@@ -938,7 +745,6 @@ If POINT is nil then called on (point)."
;;; --------------------------------------------------------
;;; Ticket browser filtering
;;; --------------------------------------------------------
-
;; See the fine manual for example code.
(defun rt-liber-default-filter-f (_ticket)
@@ -952,7 +758,6 @@ and as such always return t."
;;; --------------------------------------------------------
;;; Entry points
;;; --------------------------------------------------------
-
(defun rt-liber-browse-query (query &optional new)
"Run QUERY against the server and launch the browser.
@@ -995,7 +800,6 @@ returned as no associated text properties."
;;; --------------------------------------------------------
;;; Major mode definitions
;;; --------------------------------------------------------
-
(defun rt-liber-browser-mode-quit ()
"Bury the ticket browser."
(interactive)
@@ -1006,7 +810,7 @@ returned as no associated text properties."
(define-key map (kbd "q") 'rt-liber-browser-mode-quit)
(define-key map (kbd "n") 'rt-liber-next-ticket-in-browser)
(define-key map (kbd "p") 'rt-liber-previous-ticket-in-browser)
- (define-key map (kbd "RET") 'rt-liber-display-ticket-at-point)
+ (define-key map (kbd "RET") 'rt-liber-ticket-at-point)
(define-key map (kbd "g") 'revert-buffer)
(define-key map (kbd "G") 'rt-liber-browser-refresh-and-return)
(define-key map (kbd "a") 'rt-liber-browser-assign)
@@ -1081,8 +885,6 @@ returned as no associated text properties."
(switch-to-buffer rt-liber-browser-buffer)
(setq buffer-read-only t))
-(declare-function rt-liber-set-ancillary-text "rt-liberation-storage.el")
-
(defun rt-liber-browser-ancillary-text ()
"Wrapper function around storage backend."
(interactive)
@@ -1096,7 +898,6 @@ returned as no associated text properties."
;;; --------------------------------------------------------
;;; Command module
;;; --------------------------------------------------------
-
(defun rt-liber-command-get-dictionary-value (sym dic)
"Utility function for retrieving alist values."
(let ((value (cdr (assoc sym dic))))
@@ -1210,6 +1011,608 @@ returned as no associated text properties."
(rt-liber-browser-assign rt-liber-username))
+;;; --------------------------------------------------------
+;;; Viewer
+;;; --------------------------------------------------------
+(defun rt-liber-display-ticket-history (ticket-alist &optional assoc-browser)
+ "Display history for ticket.
+TICKET-ALIST alist of ticket data.
+ASSOC-BROWSER if non-nil should be a ticket browser."
+ (let* ((ticket-id (rt-liber-ticket-id-only ticket-alist))
+ (contents (rt-liber-rest-run-ticket-history-base-query ticket-id))
+ (new-ticket-buffer (get-buffer-create
+ (concat "*RT Ticket #" ticket-id "*"))))
+ (with-current-buffer new-ticket-buffer
+ (let ((inhibit-read-only t))
+ (erase-buffer)
+ (insert contents)
+ (goto-char (point-min))
+ (rt-liber-viewer-mode)
+ (set
+ (make-local-variable 'rt-liber-ticket-local)
+ ticket-alist)
+ (when assoc-browser
+ (set
+ (make-local-variable 'rt-liber-assoc-browser)
+ assoc-browser))
+ (set-buffer-modified-p nil)
+ (setq buffer-read-only t)))
+ (switch-to-buffer new-ticket-buffer)))
+
+
+;;; ------------------------------------------------------------------
+;;; viewer mode functions
+;;; ------------------------------------------------------------------
+(defun rt-liber-refresh-ticket-history (&optional _ignore-auto _noconfirm)
+ (interactive)
+ (if rt-liber-ticket-local
+ (rt-liber-display-ticket-history rt-liber-ticket-local
+ rt-liber-assoc-browser)
+ (error "not viewing a ticket")))
+
+(defun rt-liber-jump-to-latest-correspondence ()
+ "Move point to the newest correspondence section."
+ (interactive)
+ (let (latest-point)
+ (save-excursion
+ (goto-char (point-max))
+ (when (re-search-backward rt-liber-correspondence-regexp
+ (point-min) t)
+ (setq latest-point (point))))
+ (if latest-point
+ (progn
+ (goto-char latest-point)
+ (rt-liber-next-section-in-viewer))
+ (message "no correspondence found"))))
+
+(defun rt-liber-viewer-visit-in-browser ()
+ "Visit this ticket in the RT Web interface."
+ (interactive)
+ (let ((id (rt-liber-ticket-id-only rt-liber-ticket-local)))
+ (if id
+ (browse-url
+ (concat rt-liber-base-url "Ticket/Display.html?id=" id))
+ (error "no ticket currently in view"))))
+
+(defun rt-liber-viewer-mode-quit ()
+ "Bury the ticket viewer."
+ (interactive)
+ (bury-buffer))
+
+(defun rt-liber-viewer-show-ticket-browser ()
+ "Return to the ticket browser buffer."
+ (interactive)
+ (let ((id (rt-liber-ticket-id-only rt-liber-ticket-local)))
+ (if id
+ (let ((target-buffer
+ (if rt-liber-assoc-browser
+ (buffer-name rt-liber-assoc-browser)
+ (buffer-name rt-liber-browser-buffer-name))))
+ (if target-buffer
+ (switch-to-buffer target-buffer)
+ (error "associated ticket browser buffer no longer exists"))
+ (rt-liber-browser-move-point-to-ticket id))
+ (error "no ticket currently in view"))))
+
+(defun rt-liber-next-section-in-viewer ()
+ "Move point to next section."
+ (interactive)
+ (forward-line 1)
+ (when (not (re-search-forward rt-liber-content-regexp (point-max) t))
+ (message "no next section"))
+ (goto-char (point-at-bol)))
+
+(defun rt-liber-previous-section-in-viewer ()
+ "Move point to previous section."
+ (interactive)
+ (forward-line -1)
+ (when (not (re-search-backward rt-liber-content-regexp (point-min) t))
+ (message "no previous section"))
+ (goto-char (point-at-bol)))
+
+(defconst rt-liber-viewer-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map (kbd "q") 'rt-liber-viewer-mode-quit)
+ (define-key map (kbd "n") 'rt-liber-next-section-in-viewer)
+ (define-key map (kbd "N") 'rt-liber-jump-to-latest-correspondence)
+ (define-key map (kbd "p") 'rt-liber-previous-section-in-viewer)
+ (define-key map (kbd "V") 'rt-liber-viewer-visit-in-browser)
+ (define-key map (kbd "m") 'rt-liber-viewer-answer)
+ (define-key map (kbd "M") 'rt-liber-viewer-answer-this)
+ (define-key map (kbd "t") 'rt-liber-viewer-answer-provisionally)
+ (define-key map (kbd "T") 'rt-liber-viewer-answer-provisionally-this)
+ (define-key map (kbd "F") 'rt-liber-viewer-answer-verbatim-this)
+ (define-key map (kbd "c") 'rt-liber-viewer-comment)
+ (define-key map (kbd "C") 'rt-liber-viewer-comment-this)
+ (define-key map (kbd "g") 'revert-buffer)
+ (define-key map (kbd "SPC") 'scroll-up)
+ (define-key map (kbd "DEL") 'scroll-down)
+ (define-key map (kbd "h") 'rt-liber-viewer-show-ticket-browser)
+ map)
+ "Key map for ticket viewer.")
+
+(define-derived-mode rt-liber-viewer-mode nil
+ "RT Liberation Viewer"
+ "Major Mode for viewing RT tickets.
+\\{rt-liber-viewer-mode-map}"
+ (set
+ (make-local-variable 'font-lock-defaults)
+ '((rt-liber-viewer-font-lock-keywords)))
+ (set (make-local-variable 'revert-buffer-function)
+ #'rt-liber-refresh-ticket-history)
+ (set (make-local-variable 'buffer-stale-function)
+ (lambda (&optional _noconfirm) 'slow))
+ (when rt-liber-jump-to-latest
+ (rt-liber-jump-to-latest-correspondence))
+ (run-hooks 'rt-liber-viewer-hook))
+
+;; wrapper functions around specific functions provided by a backend
+(declare-function
+ rt-liber-gnus-compose
+ "rt-liberation-gnus.el")
+(declare-function
+ rt-liber-gnus-compose-reply-to-requestor
+ "rt-liberation-gnus.el")
+(declare-function
+ rt-liber-gnus-compose-reply-to-requestor-to-this
+ "rt-liberation-gnus.el")
+(declare-function
+ rt-liber-gnus-compose-reply-to-requestor-verbatim-this
+ "rt-liberation-gnus.el")
+(declare-function
+ rt-liber-gnus-compose-provisional
+ "rt-liberation-gnus.el")
+(declare-function
+ rt-liber-gnus-compose-provisional-to-this
+ "rt-liberation-gnus.el")
+(declare-function
+ rt-liber-gnus-compose-comment
+ "rt-liberation-gnus.el")
+(declare-function
+ rt-liber-gnus-compose-comment-this
+ "rt-liberation-gnus.el")
+
+(defun rt-liber-viewer-answer ()
+ "Answer the ticket."
+ (interactive)
+ (cond ((featurep 'rt-liberation-gnus)
+ (rt-liber-gnus-compose-reply-to-requestor))
+ (t (error "no function defined"))))
+
+(defun rt-liber-viewer-answer-this ()
+ "Answer the ticket using the current context."
+ (interactive)
+ (cond ((featurep 'rt-liberation-gnus)
+ (rt-liber-gnus-compose-reply-to-requestor-to-this))
+ (t (error "no function defined"))))
+
+(defun rt-liber-viewer-answer-verbatim-this ()
+ "Answer the ticket using the current context verbatim."
+ (interactive)
+ (cond ((featurep 'rt-liberation-gnus)
+ (rt-liber-gnus-compose-reply-to-requestor-verbatim-this))
+ (t (error "no function defined"))))
+
+(defun rt-liber-viewer-answer-provisionally ()
+ "Provisionally answer the ticket."
+ (interactive)
+ (cond ((featurep 'rt-liberation-gnus)
+ (rt-liber-gnus-compose-provisional))
+ (t (error "no function defined"))))
+
+(defun rt-liber-viewer-answer-provisionally-this ()
+ "Provisionally answer the ticket using the current context."
+ (interactive)
+ (cond ((featurep 'rt-liberation-gnus)
+ (rt-liber-gnus-compose-provisional-to-this))
+ (t (error "no function defined"))))
+
+(defun rt-liber-viewer-comment ()
+ "Comment on the ticket."
+ (interactive)
+ (cond ((featurep 'rt-liberation-gnus)
+ (rt-liber-gnus-compose-comment))
+ (t (error "no function defined"))))
+
+(defun rt-liber-viewer-comment-this ()
+ "Comment on the ticket using the current context."
+ (interactive)
+ (cond ((featurep 'rt-liberation-gnus)
+ (rt-liber-gnus-compose-comment-this))
+ (t (error "no function defined"))))
+
+
+;;; ------------------------------------------------------------------
+;;; viewer2
+;;; ------------------------------------------------------------------
+
+;; Comment: The goal is to eventually break this code away to its own
+;; file.
+
+(defface rt-liber-ticket-emph-face
+ '((((class color) (background dark))
+ (:foreground "gray53"))
+ (((class color) (background light))
+ (:foreground "gray65"))
+ (((type tty) (class mono))
+ (:inverse-video t))
+ (t (:background "Blue")))
+ "Face for important text.")
+
+(defconst rt-liber-viewer2-font-lock-keywords
+ `(("^$" 0 'rt-liber-ticket-subdued-face))
+ "Expressions to font-lock for RT ticket viewer.")
+
+
+(defun rt-liber-viewer2-vernacular-plural (time)
+ "Add an ess as needed."
+ (if (= time 1)
+ ""
+ "s"))
+
+(defun rt-liber-viewer2-vernacular-date (date)
+ "Return a vernacular time delta."
+ (let* ((now (format-time-string "%Y-%m-%dT%T%z" (current-time)))
+ (days-ago (days-between now date)))
+ (cond ((= 0 days-ago)
+ "today")
+ ((< 0 days-ago 7)
+ (format "%s day%s ago" days-ago
+ (rt-liber-viewer2-vernacular-plural days-ago)))
+ ((< 7 days-ago 30)
+ (let ((weeks (floor (/ days-ago 7.0))))
+ (format "%s week%s ago"
+ weeks
+ (rt-liber-viewer2-vernacular-plural weeks))))
+ ((< 30 days-ago 365)
+ (let ((months (floor (/ days-ago 30.0))))
+ (format "%s month%s ago"
+ months
+ (rt-liber-viewer2-vernacular-plural months))))
+ (t (let ((years (floor (/ days-ago 365.0))))
+ (format "%s year%s ago"
+ years
+ (rt-liber-viewer2-vernacular-plural years)))))))
+
+(defun rt-liber-viewer2-mode-quit ()
+ "Bury the ticket viewer."
+ (interactive)
+ (bury-buffer))
+
+(defun rt-liber-viewer-reduce (section-list f acc)
+ "A Not Invented Here tail-recursive reduce function."
+ (cond ((null (cdr section-list)) acc)
+ (t (rt-liber-viewer-reduce (cdr section-list)
+ f
+ (append acc (list
+ (funcall f
+ (car section-list)
+ (cadr section-list))))))))
+
+;; According to:
+;; "https://rt-wiki.bestpractical.com/wiki/REST#Ticket_History_Entry"
+;; id: <history-id>
+;; Ticket: <ticket-id>
+;; TimeTaken: <...>
+;; Type: <...>
+;; Field: <...>
+;; OldValue: <...>
+;; NewValue: <...>
+;; Data: <...>
+;; Description: <...>
+
+;; Content: <lin1-0>
+;; <line-1>
+;; ...
+;; <line-n>
+
+;; Creator: <...>
+;; Created: <...>
+;; Attachments: <...>
+(defun rt-liber-viewer-parse-section (start end)
+ (goto-char start)
+ (when (not (re-search-forward
+ rt-liber-viewer-section-header-regexp
+ end t))
+ (error "invalid section"))
+ (forward-line 2)
+ (let (section-field-alist
+ (rt-field-list
+ '(id Ticket TimeTaken Type Field
+ OldValue NewValue Data Description
+ Creator Created)))
+ ;; definitely error out if any of this doesn't work
+ (setq section-field-alist
+ (mapcar
+ (lambda (field-symbol)
+ (re-search-forward (format "^%s:" (symbol-name field-symbol)) end nil)
+ (cons field-symbol (buffer-substring (1+ (point)) (point-at-eol))))
+ rt-field-list))
+ ;; content
+ (goto-char start)
+ (let ((content-start (re-search-forward "^Content: " end nil))
+ (content-end (progn
+ (re-search-forward "^Creator: " end nil)
+ (point-at-bol))))
+ (append section-field-alist
+ `(,(cons 'Content
+ (buffer-substring content-start
+ content-end)))))))
+
+;; According to:
+;; "https://rt-wiki.bestpractical.com/wiki/REST#Ticket_History" is of
+;; the form: "# <n>/<n> (id/<history-id>/total)"
+(defun rt-liber-viewer-parse-history (ticket-history)
+ "Parse the string TICKET-HISTORY."
+ (when (not (stringp ticket-history))
+ (error "invalid ticket-history"))
+ (with-temp-buffer
+ (insert ticket-history)
+ (goto-char (point-min))
+ ;; find history detail sections and procude a list of section
+ ;; (start . end) pairs
+ (let (section-point-list
+ section-list)
+ (while (re-search-forward rt-liber-viewer-section-header-regexp (point-max) t)
+ (setq section-point-list (append section-point-list
+ (list (point-at-bol)))))
+ (when (not section-point-list)
+ (error "no history detail sections found"))
+ (setq section-point-list (append section-point-list
+ (list (point-max)))
+ section-point-list (rt-liber-viewer-reduce section-point-list #'cons nil))
+ ;; collect the sections
+ (setq section-list
+ (mapcar
+ (lambda (section-points)
+ (rt-liber-viewer-parse-section
+ (car section-points)
+ (cdr section-points)))
+ section-point-list))
+ section-list)))
+
+(defun rt-liber-viewer2-get-section-data ()
+ "Return the current section data."
+ (let ((section (get-text-property (point) 'rt-liberation-section-data)))
+ (when (not section)
+ (save-excursion
+ (rt-liber-viewer2-previous-section-in)
+ (setq section (get-text-property (point) 'rt-liberation-section-data))))
+ section))
+
+(defun rt-liber-viewer2-format-content (content)
+ "Wrangle RT's content format."
+ (with-temp-buffer
+ (insert content)
+ (goto-char (point-min))
+ (if (re-search-forward "^This transaction appears to have no content" (point-max) t)
+ "" ; make no content mean... no content
+ ;; trim leading blank lines
+ (save-excursion
+ (goto-char (point-min))
+ (re-search-forward "[[:graph:]]" (point-max) t)
+ (forward-line -1)
+ (flush-lines "^[[:space:]]+$" (point-min) (point)))
+ ;; trim trailing blank lines
+ (save-excursion
+ (goto-char (point-max))
+ (re-search-backward "[[:graph:]]" (point-min) t)
+ (forward-line 2)
+ (flush-lines "^[[:space:]]+$" (point-max) (point)))
+ ;; Convert the 9 leading whitespaces from RT's comment lines.
+ (goto-char (point-min))
+ (insert " ")
+ (while (re-search-forward "^ " (point-max) t)
+ (replace-match " "))
+ ;; fill
+ (let ((paragraph-separate " >[[:space:]]+$"))
+ (fill-region (point-min)
+ (point-max)))
+ ;; finally
+ (buffer-substring (point-min)
+ (point-max)))))
+
+(defun rt-liber-viewer2-clean-content (section)
+ "Format section content for email."
+ (with-temp-buffer
+ (insert (rt-liber-viewer2-format-content
+ (alist-get 'Content section)))
+ (goto-char (point-min))
+ (while (re-search-forward "^ " (point-max) t)
+ (replace-match ""))
+ ;; fill
+ (let ((paragraph-separate ">[[:space:]]+$"))
+ (fill-region (point-min)
+ (point-max)))
+ ;; finally
+ (buffer-substring (point-min)
+ (point-max))))
+
+(defun rt-liber-viewer2-display-section (section)
+ (let ((start (point))
+ (ticket-id (alist-get 'Ticket section))
+ (creator (alist-get 'Creator section))
+ (date (alist-get 'Created section))
+ (type (alist-get 'Type section))
+ (oldvalue (alist-get 'OldValue section))
+ (newvalue (alist-get 'NewValue section))
+ (field (alist-get 'Field section))
+ (s-content (rt-liber-viewer2-format-content
+ (alist-get 'Content section))))
+ (when (not (or (string= type "CommentEmailRecord")
+ (string= type "EmailRecord")))
+ (insert
+ (format "Ticket %s by %s, %s (%s) [%s]\n"
+ ticket-id
+ creator
+ (rt-liber-viewer2-vernacular-date date)
+ date
+ (format "%s%s%s"
+ type
+ (if (< 0 (length oldvalue))
+ (concat " " oldvalue)
+ "")
+ (if (< 0 (length newvalue))
+ (concat "->" newvalue)
+ ""))))
+ (add-text-properties start
+ (point)
+ `(font-lock-face rt-liber-ticket-emph-face))
+ (add-text-properties start
+ (point)
+ `(rt-liberation-viewer-header t))
+ (add-text-properties start
+ (point)
+ `(rt-liberation-section-data ,section))
+ (cond ((string= type "Status")
+ (insert
+ (format "\n Status change from %s to %s\n\n" oldvalue newvalue)))
+ ((and (string= type "Set")
+ (string= field "Owner")
+ (string= oldvalue "10"))
+ (insert
+ (format "\n Ticket assigned\n\n")))
+ ;; catch-all
+ (t
+ (insert
+ (format "\n%s\n" s-content)))))))
+
+(defun rt-liber-viewer2-display-history (contents)
+ (let ((section-list (rt-liber-viewer-parse-history contents)))
+ (mapc
+ (lambda (section)
+ (rt-liber-viewer2-display-section section))
+ section-list)))
+
+(defun rt-liber-viewer2-display-ticket-at-point ()
+ "Display the contents of the ticket at point."
+ (interactive)
+ (let ((ticket-alist (get-text-property (point) 'rt-ticket)))
+ (rt-liber-viewer2-display-ticket-history ticket-alist (current-buffer))))
+
+(defun rt-liber-viewer2-display-ticket-history (ticket-alist &optional assoc-browser)
+ "Display history for ticket.
+TICKET-ALIST alist of ticket data.
+ASSOC-BROWSER if non-nil should be a ticket browser."
+ (let* ((ticket-id (rt-liber-ticket-id-only ticket-alist))
+ (contents (rt-liber-rest-run-ticket-history-base-query ticket-id))
+ (new-ticket-buffer (get-buffer-create
+ (concat "*RT (Viewer) Ticket #" ticket-id "*"))))
+ (with-current-buffer new-ticket-buffer
+ (let ((inhibit-read-only t))
+ (erase-buffer)
+ (rt-liber-viewer2-display-history contents)
+ (goto-char (point-min))
+ (rt-liber-viewer2-mode)
+ (set
+ (make-local-variable 'rt-liber-ticket-local)
+ ticket-alist)
+ (when assoc-browser
+ (set
+ (make-local-variable 'rt-liber-assoc-browser)
+ assoc-browser))
+ (set-buffer-modified-p nil)
+ (setq buffer-read-only t)))
+ (switch-to-buffer new-ticket-buffer)))
+
+(defun rt-liber-viewer2-refresh-ticket-history (&optional _ignore-auto _noconfirm)
+ (interactive)
+ (if rt-liber-ticket-local
+ (rt-liber-viewer2-display-ticket-history rt-liber-ticket-local
+ rt-liber-assoc-browser)
+ (error "not viewing a ticket")))
+
+(defun rt-liber-viewer2-next-section-in ()
+ (interactive)
+ (when (looking-at rt-liber-viewer2-section-regexp)
+ (goto-char (1+ (point))))
+ (let ((next (re-search-forward rt-liber-viewer2-section-regexp
+ (point-max)
+ t)))
+ (cond ((not next)
+ (message "no next section"))
+ (t
+ (recenter rt-liber-viewer2-recenter)))
+ (goto-char (point-at-bol))))
+
+(defun rt-liber-viewer2-last-section-in ()
+ (interactive)
+ (goto-char (point-max))
+ (let ((last (re-search-backward rt-liber-viewer2-section-regexp
+ (point-min)
+ t)))
+ (if (not last)
+ (error "no sections found")
+ (recenter rt-liber-viewer2-recenter)
+ (goto-char (point-at-bol)))))
+
+(defun rt-liber-viewer2-previous-section-in ()
+ (interactive)
+ (when (looking-at rt-liber-viewer2-section-regexp)
+ (goto-char (1- (point))))
+ (let ((prev (re-search-backward rt-liber-viewer2-section-regexp
+ (point-min)
+ t)))
+ (cond ((not prev)
+ (message "no previous section"))
+ (t
+ (recenter rt-liber-viewer2-recenter)))
+ (goto-char (point-at-bol))))
+
+(defun rt-liber-viewer2-answer ()
+ (interactive)
+ (let ((section (rt-liber-viewer2-get-section-data)))
+ (when (not section)
+ (error "no section found"))
+ (if (not (featurep 'rt-liberation-gnus))
+ (error "rt-liberation-gnus feature not found")
+ (rt-liber-gnus-compose
+ rt-liber-gnus-address
+ rt-liber-ticket-local
+ `((contents . ,(rt-liber-viewer2-clean-content section)))))))
+
+(defun rt-liber-viewer2-comment ()
+ (interactive)
+ (let ((section (rt-liber-viewer2-get-section-data)))
+ (when (not section)
+ (error "no section found"))
+ (if (not (featurep 'rt-liberation-gnus))
+ (error "rt-liberation-gnus feature not found")
+ (rt-liber-gnus-compose
+ rt-liber-gnus-comment-address
+ rt-liber-ticket-local
+ `((contents . ,(rt-liber-viewer2-clean-content section)))))))
+
+(defconst rt-liber-viewer2-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map (kbd "q") 'rt-liber-viewer2-mode-quit)
+ (define-key map (kbd "N") 'rt-liber-viewer2-last-section-in)
+ (define-key map (kbd "n") 'rt-liber-viewer2-next-section-in)
+ (define-key map (kbd "p") 'rt-liber-viewer2-previous-section-in)
+ (define-key map (kbd "V") 'rt-liber-viewer-visit-in-browser)
+ (define-key map (kbd "M") 'rt-liber-viewer2-answer)
+ (define-key map (kbd "C") 'rt-liber-viewer2-comment)
+ (define-key map (kbd "g") 'revert-buffer)
+ (define-key map (kbd "SPC") 'scroll-up)
+ (define-key map (kbd "DEL") 'scroll-down)
+ (define-key map (kbd "h") 'rt-liber-viewer-show-ticket-browser)
+ map)
+ "Key map for ticket viewer2.")
+
+(define-derived-mode rt-liber-viewer2-mode nil
+ "RT Liberation Viewer"
+ "Major Mode for viewing RT tickets.
+\\{rt-liber-viewer-mode-map}"
+ (set
+ (make-local-variable 'font-lock-defaults)
+ '((rt-liber-viewer2-font-lock-keywords)))
+ (set (make-local-variable 'revert-buffer-function)
+ #'rt-liber-viewer2-refresh-ticket-history)
+ (set (make-local-variable 'buffer-stale-function)
+ (lambda (&optional _noconfirm) 'slow))
+ (run-hooks 'rt-liber-viewer-hook))
+
+
(provide 'rt-liberation)
;;; rt-liberation.el ends here.