* src/xml.c (parse_string): Use const.
[bpt/emacs.git] / lisp / gnus / gnus-registry.el
CommitLineData
23f87bed 1;;; gnus-registry.el --- article registry for Gnus
e84b4b86 2
c9fc72fa 3;;; Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
0ab5c2be 4;;; Free Software Foundation, Inc.
23f87bed
MB
5
6;; Author: Ted Zlatanov <tzz@lifelogs.com>
0ab5c2be 7;; Keywords: news registry
23f87bed
MB
8
9;; This file is part of GNU Emacs.
10
5e809f55 11;; GNU Emacs is free software: you can redistribute it and/or modify
23f87bed 12;; it under the terms of the GNU General Public License as published by
5e809f55
GM
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
23f87bed
MB
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
5e809f55 18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23f87bed
MB
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
5e809f55 22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
23f87bed
MB
23
24;;; Commentary:
25
01c52d31
MB
26;; This is the gnus-registry.el package, which works with all
27;; backends, not just nnmail (e.g. NNTP). The major issue is that it
28;; doesn't go across backends, so for instance if an article is in
29;; nnml:sys and you see a reference to it in nnimap splitting, the
30;; article will end up in nnimap:sys
23f87bed
MB
31
32;; gnus-registry.el intercepts article respooling, moving, deleting,
33;; and copying for all backends. If it doesn't work correctly for
34;; you, submit a bug report and I'll be glad to fix it. It needs
35;; documentation in the manual (also on my to-do list).
36
37;; Put this in your startup file (~/.gnus.el for instance)
38
39;; (setq gnus-registry-max-entries 2500
40;; gnus-registry-use-long-group-names t)
41
42;; (gnus-registry-initialize)
43
44;; Then use this in your fancy-split:
45
46;; (: gnus-registry-split-fancy-with-parent)
47
48;; TODO:
49
50;; - get the correct group on spool actions
51
52;; - articles that are spooled to a different backend should be handled
53
54;;; Code:
55
56(eval-when-compile (require 'cl))
57
58(require 'gnus)
59(require 'gnus-int)
60(require 'gnus-sum)
996aa8c1 61(require 'gnus-util)
23f87bed 62(require 'nnmail)
ec7995fa 63(require 'easymenu)
23f87bed 64
9efa445f
DN
65(defvar gnus-adaptive-word-syntax-table)
66
23f87bed
MB
67(defvar gnus-registry-dirty t
68 "Boolean set to t when the registry is modified")
69
70(defgroup gnus-registry nil
71 "The Gnus registry."
bf247b6e 72 :version "22.1"
23f87bed
MB
73 :group 'gnus)
74
c9fc72fa 75(defvar gnus-registry-hashtb (make-hash-table
01c52d31
MB
76 :size 256
77 :test 'equal)
23f87bed
MB
78 "*The article registry by Message ID.")
79
0b6799c3 80(defcustom gnus-registry-marks
14e8de0c 81 '((Important
8f7abae3
MB
82 :char ?i
83 :image "summary_important")
14e8de0c 84 (Work
8f7abae3
MB
85 :char ?w
86 :image "summary_work")
14e8de0c 87 (Personal
8f7abae3
MB
88 :char ?p
89 :image "summary_personal")
14e8de0c 90 (To-Do
8f7abae3
MB
91 :char ?t
92 :image "summary_todo")
14e8de0c 93 (Later
8f7abae3
MB
94 :char ?l
95 :image "summary_later"))
14e8de0c
MB
96
97 "List of registry marks and their options.
98
99`gnus-registry-mark-article' will offer symbols from this list
c9fc72fa 100for completion.
14e8de0c
MB
101
102Each entry must have a character to be useful for summary mode
103line display and for keyboard shortcuts.
104
105Each entry must have an image string to be useful for visual
106display."
0b6799c3 107 :group 'gnus-registry
8f7abae3
MB
108 :type '(repeat :tag "Registry Marks"
109 (cons :tag "Mark"
110 (symbol :tag "Name")
111 (checklist :tag "Options" :greedy t
112 (group :inline t
113 (const :format "" :value :char)
114 (character :tag "Character code"))
115 (group :inline t
116 (const :format "" :value :image)
117 (string :tag "Image"))))))
0b6799c3
MB
118
119(defcustom gnus-registry-default-mark 'To-Do
14e8de0c 120 "The default mark. Should be a valid key for `gnus-registry-marks'."
0b6799c3
MB
121 :group 'gnus-registry
122 :type 'symbol)
123
c9fc72fa 124(defcustom gnus-registry-unfollowed-groups
14e8de0c 125 '("delayed$" "drafts$" "queue$" "INBOX$")
01c52d31
MB
126 "List of groups that gnus-registry-split-fancy-with-parent won't return.
127The group names are matched, they don't have to be fully
128qualified. This parameter tells the Registry 'never split a
129message into a group that matches one of these, regardless of
130references.'"
23f87bed 131 :group 'gnus-registry
01c52d31 132 :type '(repeat regexp))
23f87bed 133
8f7abae3 134(defcustom gnus-registry-install 'ask
23f87bed
MB
135 "Whether the registry should be installed."
136 :group 'gnus-registry
8f7abae3
MB
137 :type '(choice (const :tag "Never Install" nil)
138 (const :tag "Always Install" t)
139 (const :tag "Ask Me" ask)))
23f87bed 140
ec7995fa
KY
141(defvar gnus-summary-misc-menu) ;; Avoid byte compiler warning.
142
143(defvar gnus-registry-misc-menus nil) ; ugly way to keep the menus
144
23f87bed
MB
145(defcustom gnus-registry-clean-empty t
146 "Whether the empty registry entries should be deleted.
01c52d31
MB
147Registry entries are considered empty when they have no groups
148and no extra data."
23f87bed
MB
149 :group 'gnus-registry
150 :type 'boolean)
151
64763fe3
MB
152(defcustom gnus-registry-use-long-group-names t
153 "Whether the registry should use long group names."
23f87bed
MB
154 :group 'gnus-registry
155 :type 'boolean)
156
b86402ab
MB
157(defcustom gnus-registry-max-track-groups 20
158 "The maximum number of non-unique group matches to check for a message ID."
159 :group 'gnus-registry
160 :type '(radio (const :format "Unlimited " nil)
161 (integer :format "Maximum non-unique matches: %v")))
162
23f87bed
MB
163(defcustom gnus-registry-track-extra nil
164 "Whether the registry should track extra data about a message.
165The Subject and Sender (From:) headers are currently tracked this
166way."
167 :group 'gnus-registry
bf247b6e 168 :type
23f87bed
MB
169 '(set :tag "Tracking choices"
170 (const :tag "Track by subject (Subject: header)" subject)
171 (const :tag "Track by sender (From: header)" sender)))
172
58a67d68
MB
173(defcustom gnus-registry-split-strategy nil
174 "Whether the registry should track extra data about a message.
175The Subject and Sender (From:) headers are currently tracked this
176way."
177 :group 'gnus-registry
178 :type
179 '(choice :tag "Tracking choices"
180 (const :tag "Only use single choices, discard multiple matches" nil)
181 (const :tag "Majority of matches wins" majority)
182 (const :tag "First found wins" first)))
183
23f87bed
MB
184(defcustom gnus-registry-entry-caching t
185 "Whether the registry should cache extra information."
186 :group 'gnus-registry
187 :type 'boolean)
188
189(defcustom gnus-registry-minimum-subject-length 5
190 "The minimum length of a subject before it's considered trackable."
191 :group 'gnus-registry
192 :type 'integer)
193
194(defcustom gnus-registry-trim-articles-without-groups t
195 "Whether the registry should clean out message IDs without groups."
196 :group 'gnus-registry
197 :type 'boolean)
198
0b6799c3
MB
199(defcustom gnus-registry-extra-entries-precious '(marks)
200 "What extra entries are precious, meaning they won't get trimmed.
201When you save the Gnus registry, it's trimmed to be no longer
202than `gnus-registry-max-entries' (which is nil by default, so no
203trimming happens). Any entries with extra data in this list (by
204default, marks are included, so articles with marks are
205considered precious) will not be trimmed."
206 :group 'gnus-registry
207 :type '(repeat symbol))
208
c9fc72fa
LMI
209(defcustom gnus-registry-cache-file
210 (nnheader-concat
211 (or gnus-dribble-directory gnus-home-directory "~/")
01c52d31 212 ".gnus.registry.eld")
23f87bed
MB
213 "File where the Gnus registry will be stored."
214 :group 'gnus-registry
215 :type 'file)
216
217(defcustom gnus-registry-max-entries nil
218 "Maximum number of entries in the registry, nil for unlimited."
219 :group 'gnus-registry
220 :type '(radio (const :format "Unlimited " nil)
ad136a7c 221 (integer :format "Maximum number: %v")))
23f87bed 222
23f87bed
MB
223(defun gnus-registry-track-subject-p ()
224 (memq 'subject gnus-registry-track-extra))
225
226(defun gnus-registry-track-sender-p ()
227 (memq 'sender gnus-registry-track-extra))
228
229(defun gnus-registry-cache-read ()
230 "Read the registry cache file."
231 (interactive)
232 (let ((file gnus-registry-cache-file))
233 (when (file-exists-p file)
234 (gnus-message 5 "Reading %s..." file)
235 (gnus-load file)
236 (gnus-message 5 "Reading %s...done" file))))
237
8aed9ac5
RS
238;; FIXME: Get rid of duplicated code, cf. `gnus-save-newsrc-file' in
239;; `gnus-start.el'. --rsteib
23f87bed
MB
240(defun gnus-registry-cache-save ()
241 "Save the registry cache file."
242 (interactive)
243 (let ((file gnus-registry-cache-file))
20a673b2 244 (with-current-buffer (gnus-get-buffer-create " *Gnus-registry-cache*")
23f87bed
MB
245 (make-local-variable 'version-control)
246 (setq version-control gnus-backup-startup-file)
247 (setq buffer-file-name file)
248 (setq default-directory (file-name-directory buffer-file-name))
249 (buffer-disable-undo)
250 (erase-buffer)
251 (gnus-message 5 "Saving %s..." file)
252 (if gnus-save-startup-file-via-temp-buffer
253 (let ((coding-system-for-write gnus-ding-file-coding-system)
254 (standard-output (current-buffer)))
c9fc72fa 255 (gnus-gnus-to-quick-newsrc-format
14e8de0c 256 t "gnus registry startup file" 'gnus-registry-alist)
23f87bed
MB
257 (gnus-registry-cache-whitespace file)
258 (save-buffer))
259 (let ((coding-system-for-write gnus-ding-file-coding-system)
260 (version-control gnus-backup-startup-file)
261 (startup-file file)
262 (working-dir (file-name-directory file))
263 working-file
264 (i -1))
265 ;; Generate the name of a non-existent file.
266 (while (progn (setq working-file
267 (format
268 (if (and (eq system-type 'ms-dos)
269 (not (gnus-long-file-names)))
270 "%s#%d.tm#" ; MSDOS limits files to 8+3
7c2fb837 271 "%s#tmp#%d")
23f87bed
MB
272 working-dir (setq i (1+ i))))
273 (file-exists-p working-file)))
bf247b6e 274
23f87bed
MB
275 (unwind-protect
276 (progn
277 (gnus-with-output-to-file working-file
c9fc72fa 278 (gnus-gnus-to-quick-newsrc-format
14e8de0c 279 t "gnus registry startup file" 'gnus-registry-alist))
bf247b6e 280
23f87bed
MB
281 ;; These bindings will mislead the current buffer
282 ;; into thinking that it is visiting the startup
283 ;; file.
284 (let ((buffer-backed-up nil)
285 (buffer-file-name startup-file)
286 (file-precious-flag t)
287 (setmodes (file-modes startup-file)))
288 ;; Backup the current version of the startup file.
289 (backup-buffer)
bf247b6e 290
23f87bed
MB
291 ;; Replace the existing startup file with the temp file.
292 (rename-file working-file startup-file t)
01c52d31 293 (gnus-set-file-modes startup-file setmodes)))
23f87bed
MB
294 (condition-case nil
295 (delete-file working-file)
296 (file-error nil)))))
bf247b6e 297
23f87bed
MB
298 (gnus-kill-buffer (current-buffer))
299 (gnus-message 5 "Saving %s...done" file))))
300
301;; Idea from Dan Christensen <jdc@chow.mat.jhu.edu>
302;; Save the gnus-registry file with extra line breaks.
303(defun gnus-registry-cache-whitespace (filename)
01c52d31 304 (gnus-message 7 "Adding whitespace to %s" filename)
23f87bed
MB
305 (save-excursion
306 (goto-char (point-min))
307 (while (re-search-forward "^(\\|(\\\"" nil t)
308 (replace-match "\n\\&" t))
309 (goto-char (point-min))
310 (while (re-search-forward " $" nil t)
311 (replace-match "" t t))))
312
313(defun gnus-registry-save (&optional force)
314 (when (or gnus-registry-dirty force)
315 (let ((caching gnus-registry-entry-caching))
316 ;; turn off entry caching, so mtime doesn't get recorded
317 (setq gnus-registry-entry-caching nil)
318 ;; remove entry caches
319 (maphash
320 (lambda (key value)
321 (if (hash-table-p value)
322 (remhash key gnus-registry-hashtb)))
323 gnus-registry-hashtb)
324 ;; remove empty entries
bf247b6e 325 (when gnus-registry-clean-empty
23f87bed 326 (gnus-registry-clean-empty-function))
01c52d31 327 ;; now trim and clean text properties from the registry appropriately
c9fc72fa 328 (setq gnus-registry-alist
01c52d31
MB
329 (gnus-registry-remove-alist-text-properties
330 (gnus-registry-trim
331 (gnus-hashtable-to-alist
332 gnus-registry-hashtb))))
23f87bed
MB
333 ;; really save
334 (gnus-registry-cache-save)
335 (setq gnus-registry-entry-caching caching)
336 (setq gnus-registry-dirty nil))))
337
338(defun gnus-registry-clean-empty-function ()
339 "Remove all empty entries from the registry. Returns count thereof."
340 (let ((count 0))
01c52d31 341
23f87bed
MB
342 (maphash
343 (lambda (key value)
01c52d31
MB
344 (when (stringp key)
345 (dolist (group (gnus-registry-fetch-groups key))
346 (when (gnus-parameter-registry-ignore group)
347 (gnus-message
c9fc72fa 348 10
01c52d31
MB
349 "gnus-registry: deleted ignored group %s from key %s"
350 group key)
351 (gnus-registry-delete-group key group)))
352
353 (unless (gnus-registry-group-count key)
354 (gnus-registry-delete-id key))
355
356 (unless (or
357 (gnus-registry-fetch-group key)
358 ;; TODO: look for specific extra data here!
359 ;; in this example, we look for 'label
360 (gnus-registry-fetch-extra key 'label))
361 (incf count)
362 (gnus-registry-delete-id key))
c9fc72fa 363
01c52d31 364 (unless (stringp key)
c9fc72fa
LMI
365 (gnus-message
366 10
367 "gnus-registry key %s was not a string, removing"
01c52d31
MB
368 key)
369 (gnus-registry-delete-id key))))
c9fc72fa 370
23f87bed
MB
371 gnus-registry-hashtb)
372 count))
373
374(defun gnus-registry-read ()
375 (gnus-registry-cache-read)
996aa8c1 376 (setq gnus-registry-hashtb (gnus-alist-to-hashtable gnus-registry-alist))
23f87bed
MB
377 (setq gnus-registry-dirty nil))
378
01c52d31
MB
379(defun gnus-registry-remove-alist-text-properties (v)
380 "Remove text properties from all strings in alist."
381 (if (stringp v)
382 (gnus-string-remove-all-properties v)
383 (if (and (listp v) (listp (cdr v)))
384 (mapcar 'gnus-registry-remove-alist-text-properties v)
385 (if (and (listp v) (stringp (cdr v)))
386 (cons (gnus-registry-remove-alist-text-properties (car v))
387 (gnus-registry-remove-alist-text-properties (cdr v)))
388 v))))
389
23f87bed 390(defun gnus-registry-trim (alist)
01c52d31 391 "Trim alist to size, using gnus-registry-max-entries.
0b6799c3 392Any entries with extra data (marks, currently) are left alone."
c9fc72fa 393 (if (null gnus-registry-max-entries)
7cb0aa56 394 alist ; just return the alist
23f87bed 395 ;; else, when given max-entries, trim the alist
7cb0aa56 396 (let* ((timehash (make-hash-table
0b6799c3
MB
397 :size 20000
398 :test 'equal))
399 (precious (make-hash-table
400 :size 20000
7cb0aa56
MB
401 :test 'equal))
402 (trim-length (- (length alist) gnus-registry-max-entries))
0b6799c3
MB
403 (trim-length (if (natnump trim-length) trim-length 0))
404 precious-list junk-list)
23f87bed
MB
405 (maphash
406 (lambda (key value)
0b6799c3
MB
407 (let ((extra (gnus-registry-fetch-extra key)))
408 (dolist (item gnus-registry-extra-entries-precious)
409 (dolist (e extra)
410 (when (equal (nth 0 e) item)
411 (puthash key t precious)
412 (return))))
413 (puthash key (gnus-registry-fetch-extra key 'mtime) timehash)))
23f87bed 414 gnus-registry-hashtb)
23f87bed 415
0b6799c3 416 (dolist (item alist)
3d0f8a67 417 (let ((key (nth 0 item)))
0b6799c3
MB
418 (if (gethash key precious)
419 (push item precious-list)
420 (push item junk-list))))
421
c9fc72fa 422 (sort
0b6799c3
MB
423 junk-list
424 (lambda (a b)
c9fc72fa 425 (let ((t1 (or (cdr (gethash (car a) timehash))
0b6799c3 426 '(0 0 0)))
c9fc72fa 427 (t2 (or (cdr (gethash (car b) timehash))
0b6799c3
MB
428 '(0 0 0))))
429 (time-less-p t1 t2))))
430
431 ;; we use the return value of this setq, which is the trimmed alist
432 (setq alist (append precious-list
433 (nthcdr trim-length junk-list))))))
c9fc72fa 434
23f87bed
MB
435(defun gnus-registry-action (action data-header from &optional to method)
436 (let* ((id (mail-header-id data-header))
01c52d31
MB
437 (subject (gnus-string-remove-all-properties
438 (gnus-registry-simplify-subject
439 (mail-header-subject data-header))))
c9fc72fa 440 (sender (gnus-string-remove-all-properties
14e8de0c 441 (mail-header-from data-header)))
23f87bed
MB
442 (from (gnus-group-guess-full-name-from-command-method from))
443 (to (if to (gnus-group-guess-full-name-from-command-method to) nil))
444 (to-name (if to to "the Bit Bucket"))
445 (old-entry (gethash id gnus-registry-hashtb)))
01c52d31 446 (gnus-message 7 "Registry: article %s %s from %s to %s"
23f87bed
MB
447 id
448 (if method "respooling" "going")
449 from
450 to)
451
452 ;; All except copy will need a delete
453 (gnus-registry-delete-group id from)
454
bf247b6e 455 (when (equal 'copy action)
23f87bed
MB
456 (gnus-registry-add-group id from subject sender)) ; undo the delete
457
458 (gnus-registry-add-group id to subject sender)))
459
460(defun gnus-registry-spool-action (id group &optional subject sender)
461 (let ((group (gnus-group-guess-full-name-from-command-method group)))
462 (when (and (stringp id) (string-match "\r$" id))
463 (setq id (substring id 0 -1)))
01c52d31 464 (gnus-message 7 "Registry: article %s spooled to %s"
23f87bed
MB
465 id
466 group)
467 (gnus-registry-add-group id group subject sender)))
468
469;; Function for nn{mail|imap}-split-fancy: look up all references in
470;; the cache and if a match is found, return that group.
471(defun gnus-registry-split-fancy-with-parent ()
472 "Split this message into the same group as its parent. The parent
473is obtained from the registry. This function can be used as an entry
474in `nnmail-split-fancy' or `nnimap-split-fancy', for example like
bf247b6e 475this: (: gnus-registry-split-fancy-with-parent)
23f87bed 476
01c52d31
MB
477This function tracks ALL backends, unlike
478`nnmail-split-fancy-with-parent' which tracks only nnmail
479messages.
480
23f87bed 481For a message to be split, it looks for the parent message in the
01c52d31
MB
482References or In-Reply-To header and then looks in the registry
483to see which group that message was put in. This group is
14e8de0c
MB
484returned, unless `gnus-registry-follow-group-p' return nil for
485that group.
23f87bed
MB
486
487See the Info node `(gnus)Fancy Mail Splitting' for more details."
14e8de0c
MB
488 (let* ((refstr (or (message-fetch-field "references") "")) ; guaranteed
489 (reply-to (message-fetch-field "in-reply-to")) ; may be nil
01c52d31 490 ;; now, if reply-to is valid, append it to the References
c9fc72fa 491 (refstr (if reply-to
01c52d31
MB
492 (concat refstr " " reply-to)
493 refstr))
14e8de0c
MB
494 ;; these may not be used, but the code is cleaner having them up here
495 (sender (gnus-string-remove-all-properties
496 (message-fetch-field "from")))
497 (subject (gnus-string-remove-all-properties
498 (gnus-registry-simplify-subject
499 (message-fetch-field "subject"))))
500
501 (nnmail-split-fancy-with-parent-ignore-groups
502 (if (listp nnmail-split-fancy-with-parent-ignore-groups)
503 nnmail-split-fancy-with-parent-ignore-groups
504 (list nnmail-split-fancy-with-parent-ignore-groups)))
505 (log-agent "gnus-registry-split-fancy-with-parent")
58a67d68 506 found found-full)
14e8de0c
MB
507
508 ;; this is a big if-else statement. it uses
509 ;; gnus-registry-post-process-groups to filter the results after
510 ;; every step.
511 (cond
512 ;; the references string must be valid and parse to valid references
513 ((and refstr (gnus-extract-references refstr))
514 (dolist (reference (nreverse (gnus-extract-references refstr)))
515 (gnus-message
516 9
517 "%s is looking for matches for reference %s from [%s]"
518 log-agent reference refstr)
c9fc72fa
LMI
519 (dolist (group (gnus-registry-fetch-groups
520 reference
b86402ab 521 gnus-registry-max-track-groups))
14e8de0c 522 (when (and group (gnus-registry-follow-group-p group))
23f87bed 523 (gnus-message
14e8de0c
MB
524 7
525 "%s traced the reference %s from [%s] to group %s"
526 log-agent reference refstr group)
527 (push group found))))
528 ;; filter the found groups and return them
58a67d68 529 ;; the found groups are the full groups
c9fc72fa 530 (setq found (gnus-registry-post-process-groups
58a67d68 531 "references" refstr found found)))
c9fc72fa 532
14e8de0c 533 ;; else: there were no matches, now try the extra tracking by sender
8336c962
MB
534 ((and (gnus-registry-track-sender-p)
535 sender
0ab5c2be
MB
536 (not (equal (gnus-extract-address-component-email sender)
537 user-mail-address)))
14e8de0c
MB
538 (maphash
539 (lambda (key value)
540 (let ((this-sender (cdr
541 (gnus-registry-fetch-extra key 'sender)))
542 matches)
543 (when (and this-sender
544 (equal sender this-sender))
c9fc72fa 545 (let ((groups (gnus-registry-fetch-groups
b86402ab
MB
546 key
547 gnus-registry-max-track-groups)))
9b3ebcb6 548 (dolist (group groups)
58a67d68 549 (push group found-full)
9b3ebcb6 550 (setq found (append (list group) (delete group found)))))
14e8de0c
MB
551 (push key matches)
552 (gnus-message
553 ;; raise level of messaging if gnus-registry-track-extra
554 (if gnus-registry-track-extra 7 9)
555 "%s (extra tracking) traced sender %s to groups %s (keys %s)"
556 log-agent sender found matches))))
557 gnus-registry-hashtb)
558 ;; filter the found groups and return them
58a67d68 559 ;; the found groups are NOT the full groups
c9fc72fa 560 (setq found (gnus-registry-post-process-groups
58a67d68 561 "sender" sender found found-full)))
c9fc72fa 562
14e8de0c
MB
563 ;; else: there were no matches, now try the extra tracking by subject
564 ((and (gnus-registry-track-subject-p)
565 subject
566 (< gnus-registry-minimum-subject-length (length subject)))
567 (maphash
568 (lambda (key value)
569 (let ((this-subject (cdr
570 (gnus-registry-fetch-extra key 'subject)))
571 matches)
572 (when (and this-subject
573 (equal subject this-subject))
c9fc72fa 574 (let ((groups (gnus-registry-fetch-groups
b86402ab
MB
575 key
576 gnus-registry-max-track-groups)))
9b3ebcb6 577 (dolist (group groups)
58a67d68 578 (push group found-full)
9b3ebcb6 579 (setq found (append (list group) (delete group found)))))
14e8de0c
MB
580 (push key matches)
581 (gnus-message
582 ;; raise level of messaging if gnus-registry-track-extra
583 (if gnus-registry-track-extra 7 9)
584 "%s (extra tracking) traced subject %s to groups %s (keys %s)"
585 log-agent subject found matches))))
586 gnus-registry-hashtb)
587 ;; filter the found groups and return them
58a67d68 588 ;; the found groups are NOT the full groups
c9fc72fa 589 (setq found (gnus-registry-post-process-groups
58a67d68
MB
590 "subject" subject found found-full))))
591 ;; after the (cond) we extract the actual value safely
592 (car-safe found)))
14e8de0c 593
58a67d68 594(defun gnus-registry-post-process-groups (mode key groups groups-full)
14e8de0c
MB
595 "Modifies GROUPS found by MODE for KEY to determine which ones to follow.
596
597MODE can be 'subject' or 'sender' for example. The KEY is the
598value by which MODE was searched.
599
600Transforms each group name to the equivalent short name.
601
602Checks if the current Gnus method (from `gnus-command-method' or
603from `gnus-newsgroup-name') is the same as the group's method.
604This is not possible if gnus-registry-use-long-group-names is
605false. Foreign methods are not supported so they are rejected.
606
607Reduces the list to a single group, or complains if that's not
58a67d68
MB
608possible. Uses `gnus-registry-split-strategy' and GROUPS-FULL if
609necessary."
14e8de0c
MB
610 (let ((log-agent "gnus-registry-post-process-group")
611 out)
58a67d68
MB
612
613 ;; the strategy can be 'first, 'majority, or nil
614 (when (eq gnus-registry-split-strategy 'first)
615 (when groups
616 (setq groups (list (car-safe groups)))))
617
618 (when (eq gnus-registry-split-strategy 'majority)
619 (let ((freq (make-hash-table
620 :size 256
621 :test 'equal)))
622 (mapc (lambda(x) (puthash x (1+ (gethash x freq 0)) freq)) groups-full)
623 (setq groups (list (car-safe
624 (sort
625 groups
626 (lambda (a b)
627 (> (gethash a freq 0)
628 (gethash b freq 0)))))))))
c9fc72fa 629
14e8de0c
MB
630 (if gnus-registry-use-long-group-names
631 (dolist (group groups)
632 (let ((m1 (gnus-find-method-for-group group))
633 (m2 (or gnus-command-method
634 (gnus-find-method-for-group gnus-newsgroup-name)))
635 (short-name (gnus-group-short-name group)))
636 (if (gnus-methods-equal-p m1 m2)
637 (progn
638 ;; this is REALLY just for debugging
639 (gnus-message
640 10
641 "%s stripped group %s to %s"
642 log-agent group short-name)
643 (unless (member short-name out)
644 (push short-name out)))
645 ;; else...
646 (gnus-message
647 7
648 "%s ignored foreign group %s"
649 log-agent group))))
650 (setq out groups))
651 (when (cdr-safe out)
23f87bed 652 (gnus-message
14e8de0c
MB
653 5
654 "%s: too many extra matches (%s) for %s %s. Returning none."
655 log-agent out mode key)
656 (setq out nil))
657 out))
658
659(defun gnus-registry-follow-group-p (group)
660 "Determines if a group name should be followed.
661Consults `gnus-registry-unfollowed-groups' and
662`nnmail-split-fancy-with-parent-ignore-groups'."
77154961 663 (not (or (gnus-grep-in-list
14e8de0c
MB
664 group
665 gnus-registry-unfollowed-groups)
77154961 666 (gnus-grep-in-list
14e8de0c
MB
667 group
668 nnmail-split-fancy-with-parent-ignore-groups))))
23f87bed 669
01c52d31
MB
670(defun gnus-registry-wash-for-keywords (&optional force)
671 (interactive)
672 (let ((id (gnus-registry-fetch-message-id-fast gnus-current-article))
673 word words)
674 (if (or (not (gnus-registry-fetch-extra id 'keywords))
675 force)
20a673b2 676 (with-current-buffer gnus-article-buffer
01c52d31
MB
677 (article-goto-body)
678 (save-window-excursion
679 (save-restriction
680 (narrow-to-region (point) (point-max))
681 (with-syntax-table gnus-adaptive-word-syntax-table
682 (while (re-search-forward "\\b\\w+\\b" nil t)
683 (setq word (gnus-registry-remove-alist-text-properties
684 (downcase (buffer-substring
685 (match-beginning 0) (match-end 0)))))
686 (if (> (length word) 3)
687 (push word words))))))
688 (gnus-registry-store-extra-entry id 'keywords words)))))
689
690(defun gnus-registry-find-keywords (keyword)
691 (interactive "skeyword: ")
692 (let (articles)
693 (maphash
694 (lambda (key value)
0ab5c2be
MB
695 (when (member keyword
696 (cdr-safe (gnus-registry-fetch-extra key 'keywords)))
01c52d31
MB
697 (push key articles)))
698 gnus-registry-hashtb)
699 articles))
700
23f87bed
MB
701(defun gnus-registry-register-message-ids ()
702 "Register the Message-ID of every article in the group"
703 (unless (gnus-parameter-registry-ignore gnus-newsgroup-name)
704 (dolist (article gnus-newsgroup-articles)
705 (let ((id (gnus-registry-fetch-message-id-fast article)))
3d0f8a67 706 (unless (member gnus-newsgroup-name (gnus-registry-fetch-groups id))
bf247b6e 707 (gnus-message 9 "Registry: Registering article %d with group %s"
23f87bed 708 article gnus-newsgroup-name)
c9fc72fa
LMI
709 (gnus-registry-add-group
710 id
23f87bed
MB
711 gnus-newsgroup-name
712 (gnus-registry-fetch-simplified-message-subject-fast article)
713 (gnus-registry-fetch-sender-fast article)))))))
714
715(defun gnus-registry-fetch-message-id-fast (article)
716 "Fetch the Message-ID quickly, using the internal gnus-data-list function"
717 (if (and (numberp article)
718 (assoc article (gnus-data-list nil)))
719 (mail-header-id (gnus-data-header (assoc article (gnus-data-list nil))))
720 nil))
721
722(defun gnus-registry-simplify-subject (subject)
723 (if (stringp subject)
724 (gnus-simplify-subject subject)
725 nil))
726
727(defun gnus-registry-fetch-simplified-message-subject-fast (article)
728 "Fetch the Subject quickly, using the internal gnus-data-list function"
729 (if (and (numberp article)
730 (assoc article (gnus-data-list nil)))
01c52d31
MB
731 (gnus-string-remove-all-properties
732 (gnus-registry-simplify-subject
733 (mail-header-subject (gnus-data-header
734 (assoc article (gnus-data-list nil))))))
23f87bed
MB
735 nil))
736
737(defun gnus-registry-fetch-sender-fast (article)
738 "Fetch the Sender quickly, using the internal gnus-data-list function"
739 (if (and (numberp article)
740 (assoc article (gnus-data-list nil)))
01c52d31
MB
741 (gnus-string-remove-all-properties
742 (mail-header-from (gnus-data-header
743 (assoc article (gnus-data-list nil)))))
23f87bed
MB
744 nil))
745
14e8de0c
MB
746(defun gnus-registry-do-marks (type function)
747 "For each known mark, call FUNCTION for each cell of type TYPE.
748
749FUNCTION should take two parameters, a mark symbol and the cell value."
750 (dolist (mark-info gnus-registry-marks)
8f7abae3
MB
751 (let* ((mark (car-safe mark-info))
752 (data (cdr-safe mark-info))
753 (cell-data (plist-get data type)))
754 (when cell-data
755 (funcall function mark cell-data)))))
14e8de0c
MB
756
757;;; this is ugly code, but I don't know how to do it better
8f7abae3 758(defun gnus-registry-install-shortcuts ()
14e8de0c
MB
759 "Install the keyboard shortcuts and menus for the registry.
760Uses `gnus-registry-marks' to find what shortcuts to install."
8f7abae3 761 (let (keys-plist)
ec7995fa
KY
762 (setq gnus-registry-misc-menus nil)
763 (gnus-registry-do-marks
8f7abae3
MB
764 :char
765 (lambda (mark data)
766 (let ((function-format
767 (format "gnus-registry-%%s-article-%s-mark" mark)))
14e8de0c
MB
768
769;;; The following generates these functions:
770;;; (defun gnus-registry-set-article-Important-mark (&rest articles)
771;;; "Apply the Important mark to process-marked ARTICLES."
772;;; (interactive (gnus-summary-work-articles current-prefix-arg))
773;;; (gnus-registry-set-article-mark-internal 'Important articles nil t))
774;;; (defun gnus-registry-remove-article-Important-mark (&rest articles)
775;;; "Apply the Important mark to process-marked ARTICLES."
776;;; (interactive (gnus-summary-work-articles current-prefix-arg))
777;;; (gnus-registry-set-article-mark-internal 'Important articles t t))
778
8f7abae3
MB
779 (dolist (remove '(t nil))
780 (let* ((variant-name (if remove "remove" "set"))
781 (function-name (format function-format variant-name))
782 (shortcut (format "%c" data))
783 (shortcut (if remove (upcase shortcut) shortcut)))
5e2a84e3 784 (unintern function-name obarray)
8f7abae3 785 (eval
c9fc72fa 786 `(defun
8f7abae3 787 ;; function name
c9fc72fa 788 ,(intern function-name)
8f7abae3
MB
789 ;; parameter definition
790 (&rest articles)
791 ;; documentation
c9fc72fa 792 ,(format
8f7abae3
MB
793 "%s the %s mark over process-marked ARTICLES."
794 (upcase-initials variant-name)
795 mark)
796 ;; interactive definition
c9fc72fa 797 (interactive
8f7abae3
MB
798 (gnus-summary-work-articles current-prefix-arg))
799 ;; actual code
800
801 ;; if this is called and the user doesn't want the
802 ;; registry enabled, we'll ask anyhow
803 (when (eq gnus-registry-install nil)
804 (setq gnus-registry-install 'ask))
805
806 ;; now the user is asked if gnus-registry-install is 'ask
807 (when (gnus-registry-install-p)
c9fc72fa 808 (gnus-registry-set-article-mark-internal
8f7abae3
MB
809 ;; all this just to get the mark, I must be doing it wrong
810 (intern ,(symbol-name mark))
811 articles ,remove t)
ec7995fa 812 (gnus-message
c9fc72fa 813 9
ec7995fa
KY
814 "Applying mark %s to %d articles"
815 ,(symbol-name mark) (length articles))
8f7abae3 816 (dolist (article articles)
c9fc72fa 817 (gnus-summary-update-article
ec7995fa 818 article
8f7abae3
MB
819 (assoc article (gnus-data-list nil)))))))
820 (push (intern function-name) keys-plist)
c9fc72fa 821 (push shortcut keys-plist)
ec7995fa
KY
822 (push (vector (format "%s %s"
823 (upcase-initials variant-name)
824 (symbol-name mark))
825 (intern function-name) t)
826 gnus-registry-misc-menus)
827 (gnus-message
c9fc72fa
LMI
828 9
829 "Defined mark handling function %s"
8f7abae3
MB
830 function-name))))))
831 (gnus-define-keys-1
ec7995fa
KY
832 '(gnus-registry-mark-map "M" gnus-summary-mark-map)
833 keys-plist)
834 (add-hook 'gnus-summary-menu-hook
835 (lambda ()
c9fc72fa 836 (easy-menu-add-item
ec7995fa 837 gnus-summary-misc-menu
c9fc72fa 838 nil
ec7995fa 839 (cons "Registry Marks" gnus-registry-misc-menus))))))
8f7abae3
MB
840
841;;; use like this:
c9fc72fa 842;;; (defalias 'gnus-user-format-function-M
8f7abae3
MB
843;;; 'gnus-registry-user-format-function-M)
844(defun gnus-registry-user-format-function-M (headers)
845 (let* ((id (mail-header-message-id headers))
846 (marks (when id (gnus-registry-fetch-extra-marks id))))
847 (apply 'concat (mapcar (lambda(mark)
c9fc72fa 848 (let ((c
8f7abae3 849 (plist-get
c9fc72fa 850 (cdr-safe
8f7abae3
MB
851 (assoc mark gnus-registry-marks))
852 :char)))
853 (if c
854 (list c)
855 nil)))
856 marks))))
0b6799c3
MB
857
858(defun gnus-registry-read-mark ()
859 "Read a mark name from the user with completion."
229b59da
G
860 (let ((mark (gnus-completing-read
861 "Label"
862 (mapcar 'symbol-name (mapcar 'car gnus-registry-marks))
863 nil nil nil
864 (symbol-name gnus-registry-default-mark))))
0b6799c3
MB
865 (when (stringp mark)
866 (intern mark))))
867
868(defun gnus-registry-set-article-mark (&rest articles)
869 "Apply a mark to process-marked ARTICLES."
870 (interactive (gnus-summary-work-articles current-prefix-arg))
871 (gnus-registry-set-article-mark-internal (gnus-registry-read-mark) articles nil t))
872
873(defun gnus-registry-remove-article-mark (&rest articles)
874 "Remove a mark from process-marked ARTICLES."
875 (interactive (gnus-summary-work-articles current-prefix-arg))
876 (gnus-registry-set-article-mark-internal (gnus-registry-read-mark) articles t t))
877
878(defun gnus-registry-set-article-mark-internal (mark articles &optional remove show-message)
879 "Apply a mark to a list of ARTICLES."
880 (let ((article-id-list
881 (mapcar 'gnus-registry-fetch-message-id-fast articles)))
882 (dolist (id article-id-list)
883 (let* (
884 ;; all the marks for this article without the mark of
885 ;; interest
886 (marks
887 (delq mark (gnus-registry-fetch-extra-marks id)))
888 ;; the new marks we want to use
889 (new-marks (if remove
890 marks
891 (cons mark marks))))
892 (when show-message
893 (gnus-message 1 "%s mark %s with message ID %s, resulting in %S"
894 (if remove "Removing" "Adding")
895 mark id new-marks))
c9fc72fa 896
0b6799c3
MB
897 (apply 'gnus-registry-store-extra-marks ; set the extra marks
898 id ; for the message ID
899 new-marks)))))
900
901(defun gnus-registry-get-article-marks (&rest articles)
902 "Get the Gnus registry marks for ARTICLES and show them if interactive.
903Uses process/prefix conventions. For multiple articles,
904only the last one's marks are returned."
905 (interactive (gnus-summary-work-articles 1))
906 (let (marks)
907 (dolist (article articles)
908 (let ((article-id
909 (gnus-registry-fetch-message-id-fast article)))
910 (setq marks (gnus-registry-fetch-extra-marks article-id))))
911 (when (interactive-p)
912 (gnus-message 1 "Marks are %S" marks))
913 marks))
914
915;;; if this extends to more than 'marks, it should be improved to be more generic.
916(defun gnus-registry-fetch-extra-marks (id)
917 "Get the marks of a message, based on the message ID.
918Returns a list of symbol marks or nil."
919 (car-safe (cdr (gnus-registry-fetch-extra id 'marks))))
920
921(defun gnus-registry-has-extra-mark (id mark)
922 "Checks if a message has `mark', based on the message ID `id'."
923 (memq mark (gnus-registry-fetch-extra-marks id)))
924
925(defun gnus-registry-store-extra-marks (id &rest mark-list)
926 "Set the marks of a message, based on the message ID.
927The `mark-list' can be nil, in which case no marks are left."
928 (gnus-registry-store-extra-entry id 'marks (list mark-list)))
929
930(defun gnus-registry-delete-extra-marks (id &rest mark-delete-list)
931 "Delete the message marks in `mark-delete-list', based on the message ID."
932 (let ((marks (gnus-registry-fetch-extra-marks id)))
933 (when marks
934 (dolist (mark mark-delete-list)
935 (setq marks (delq mark marks))))
936 (gnus-registry-store-extra-marks id (car marks))))
937
938(defun gnus-registry-delete-all-extra-marks (id)
939 "Delete all the marks for a message ID."
940 (gnus-registry-store-extra-marks id nil))
01c52d31 941
23f87bed
MB
942(defun gnus-registry-fetch-extra (id &optional entry)
943 "Get the extra data of a message, based on the message ID.
944Returns the first place where the trail finds a nonstring."
945 (let ((entry-cache (gethash entry gnus-registry-hashtb)))
946 (if (and entry
947 (hash-table-p entry-cache)
948 (gethash id entry-cache))
949 (gethash id entry-cache)
950 ;; else, if there is no caching possible...
951 (let ((trail (gethash id gnus-registry-hashtb)))
952 (when (listp trail)
953 (dolist (crumb trail)
954 (unless (stringp crumb)
955 (return (gnus-registry-fetch-extra-entry crumb entry id)))))))))
956
957(defun gnus-registry-fetch-extra-entry (alist &optional entry id)
958 "Get the extra data of a message, or a specific entry in it.
959Update the entry cache if needed."
960 (if (and entry id)
961 (let ((entry-cache (gethash entry gnus-registry-hashtb))
962 entree)
963 (when gnus-registry-entry-caching
964 ;; create the hash table
965 (unless (hash-table-p entry-cache)
966 (setq entry-cache (make-hash-table
967 :size 4096
968 :test 'equal))
969 (puthash entry entry-cache gnus-registry-hashtb))
970
971 ;; get the entree from the hash table or from the alist
972 (setq entree (gethash id entry-cache)))
bf247b6e 973
23f87bed
MB
974 (unless entree
975 (setq entree (assq entry alist))
976 (when gnus-registry-entry-caching
977 (puthash id entree entry-cache)))
978 entree)
979 alist))
980
981(defun gnus-registry-store-extra (id extra)
982 "Store the extra data of a message, based on the message ID.
983The message must have at least one group name."
984 (when (gnus-registry-group-count id)
985 ;; we now know the trail has at least 1 group name, so it's not empty
986 (let ((trail (gethash id gnus-registry-hashtb))
987 (old-extra (gnus-registry-fetch-extra id))
988 entry-cache)
989 (dolist (crumb trail)
990 (unless (stringp crumb)
991 (dolist (entry crumb)
992 (setq entry-cache (gethash (car entry) gnus-registry-hashtb))
993 (when entry-cache
994 (remhash id entry-cache))))
995 (puthash id (cons extra (delete old-extra trail))
996 gnus-registry-hashtb)
997 (setq gnus-registry-dirty t)))))
998
01c52d31
MB
999(defun gnus-registry-delete-extra-entry (id key)
1000 "Delete a specific entry in the extras field of the registry entry for id."
1001 (gnus-registry-store-extra-entry id key nil))
1002
23f87bed
MB
1003(defun gnus-registry-store-extra-entry (id key value)
1004 "Put a specific entry in the extras field of the registry entry for id."
1005 (let* ((extra (gnus-registry-fetch-extra id))
01c52d31 1006 ;; all the entries except the one for `key'
c9fc72fa 1007 (the-rest (gnus-assq-delete-all key (gnus-registry-fetch-extra id)))
01c52d31
MB
1008 (alist (if value
1009 (gnus-registry-remove-alist-text-properties
1010 (cons (cons key value)
1011 the-rest))
1012 the-rest)))
23f87bed
MB
1013 (gnus-registry-store-extra id alist)))
1014
1015(defun gnus-registry-fetch-group (id)
1016 "Get the group of a message, based on the message ID.
1017Returns the first place where the trail finds a group name."
1018 (when (gnus-registry-group-count id)
1019 ;; we now know the trail has at least 1 group name
1020 (let ((trail (gethash id gnus-registry-hashtb)))
1021 (dolist (crumb trail)
1022 (when (stringp crumb)
bf247b6e
KS
1023 (return (if gnus-registry-use-long-group-names
1024 crumb
23f87bed
MB
1025 (gnus-group-short-name crumb))))))))
1026
b86402ab
MB
1027(defun gnus-registry-fetch-groups (id &optional max)
1028 "Get the groups (up to MAX, if given) of a message, based on the message ID."
01c52d31
MB
1029 (let ((trail (gethash id gnus-registry-hashtb))
1030 groups)
1031 (dolist (crumb trail)
1032 (when (stringp crumb)
1033 ;; push the group name into the list
c9fc72fa 1034 (setq
01c52d31
MB
1035 groups
1036 (cons
1037 (if (or (not (stringp crumb)) gnus-registry-use-long-group-names)
1038 crumb
1039 (gnus-group-short-name crumb))
b86402ab
MB
1040 groups))
1041 (when (and max (> (length groups) max))
1042 (return))))
01c52d31
MB
1043 ;; return the list of groups
1044 groups))
1045
23f87bed
MB
1046(defun gnus-registry-group-count (id)
1047 "Get the number of groups of a message, based on the message ID."
1048 (let ((trail (gethash id gnus-registry-hashtb)))
1049 (if (and trail (listp trail))
1050 (apply '+ (mapcar (lambda (x) (if (stringp x) 1 0)) trail))
1051 0)))
1052
1053(defun gnus-registry-delete-group (id group)
1054 "Delete a group for a message, based on the message ID."
01c52d31 1055 (when (and group id)
23f87bed 1056 (let ((trail (gethash id gnus-registry-hashtb))
01c52d31 1057 (short-group (gnus-group-short-name group)))
23f87bed 1058 (puthash id (if trail
01c52d31 1059 (delete short-group (delete group trail))
23f87bed
MB
1060 nil)
1061 gnus-registry-hashtb))
1062 ;; now, clear the entry if there are no more groups
1063 (when gnus-registry-trim-articles-without-groups
1064 (unless (gnus-registry-group-count id)
1065 (gnus-registry-delete-id id)))
270a576a
MB
1066 ;; is this ID still in the registry?
1067 (when (gethash id gnus-registry-hashtb)
01c52d31 1068 (gnus-registry-store-extra-entry id 'mtime (current-time)))))
23f87bed
MB
1069
1070(defun gnus-registry-delete-id (id)
1071 "Delete a message ID from the registry."
1072 (when (stringp id)
1073 (remhash id gnus-registry-hashtb)
1074 (maphash
1075 (lambda (key value)
1076 (when (hash-table-p value)
1077 (remhash id value)))
1078 gnus-registry-hashtb)))
1079
1080(defun gnus-registry-add-group (id group &optional subject sender)
1081 "Add a group for a message, based on the message ID."
1082 (when group
1083 (when (and id
1084 (not (string-match "totally-fudged-out-message-id" id)))
1085 (let ((full-group group)
bf247b6e
KS
1086 (group (if gnus-registry-use-long-group-names
1087 group
23f87bed
MB
1088 (gnus-group-short-name group))))
1089 (gnus-registry-delete-group id group)
1090
1091 (unless gnus-registry-use-long-group-names ;; unnecessary in this case
1092 (gnus-registry-delete-group id full-group))
1093
1094 (let ((trail (gethash id gnus-registry-hashtb)))
1095 (puthash id (if trail
1096 (cons group trail)
1097 (list group))
1098 gnus-registry-hashtb)
1099
1100 (when (and (gnus-registry-track-subject-p)
1101 subject)
1102 (gnus-registry-store-extra-entry
bf247b6e
KS
1103 id
1104 'subject
23f87bed
MB
1105 (gnus-registry-simplify-subject subject)))
1106 (when (and (gnus-registry-track-sender-p)
1107 sender)
1108 (gnus-registry-store-extra-entry
bf247b6e 1109 id
23f87bed
MB
1110 'sender
1111 sender))
bf247b6e 1112
23f87bed
MB
1113 (gnus-registry-store-extra-entry id 'mtime (current-time)))))))
1114
1115(defun gnus-registry-clear ()
1116 "Clear the Gnus registry."
1117 (interactive)
1118 (setq gnus-registry-alist nil)
996aa8c1 1119 (setq gnus-registry-hashtb (gnus-alist-to-hashtable gnus-registry-alist))
23f87bed
MB
1120 (setq gnus-registry-dirty t))
1121
1122;;;###autoload
1123(defun gnus-registry-initialize ()
8f7abae3 1124"Initialize the Gnus registry."
23f87bed 1125 (interactive)
8f7abae3
MB
1126 (gnus-message 5 "Initializing the registry")
1127 (setq gnus-registry-install t) ; in case it was 'ask or nil
23f87bed 1128 (gnus-registry-install-hooks)
8f7abae3 1129 (gnus-registry-install-shortcuts)
23f87bed
MB
1130 (gnus-registry-read))
1131
1132;;;###autoload
1133(defun gnus-registry-install-hooks ()
1134 "Install the registry hooks."
1135 (interactive)
bf247b6e 1136 (add-hook 'gnus-summary-article-move-hook 'gnus-registry-action)
23f87bed
MB
1137 (add-hook 'gnus-summary-article-delete-hook 'gnus-registry-action)
1138 (add-hook 'gnus-summary-article-expire-hook 'gnus-registry-action)
1139 (add-hook 'nnmail-spool-hook 'gnus-registry-spool-action)
bf247b6e 1140
23f87bed
MB
1141 (add-hook 'gnus-save-newsrc-hook 'gnus-registry-save)
1142 (add-hook 'gnus-read-newsrc-el-hook 'gnus-registry-read)
1143
1144 (add-hook 'gnus-summary-prepare-hook 'gnus-registry-register-message-ids))
1145
1146(defun gnus-registry-unload-hook ()
1147 "Uninstall the registry hooks."
1148 (interactive)
bf247b6e 1149 (remove-hook 'gnus-summary-article-move-hook 'gnus-registry-action)
23f87bed
MB
1150 (remove-hook 'gnus-summary-article-delete-hook 'gnus-registry-action)
1151 (remove-hook 'gnus-summary-article-expire-hook 'gnus-registry-action)
1152 (remove-hook 'nnmail-spool-hook 'gnus-registry-spool-action)
bf247b6e 1153
23f87bed
MB
1154 (remove-hook 'gnus-save-newsrc-hook 'gnus-registry-save)
1155 (remove-hook 'gnus-read-newsrc-el-hook 'gnus-registry-read)
1156
1157 (remove-hook 'gnus-summary-prepare-hook 'gnus-registry-register-message-ids))
1158
6d52545d
RS
1159(add-hook 'gnus-registry-unload-hook 'gnus-registry-unload-hook)
1160
8f7abae3
MB
1161(defun gnus-registry-install-p ()
1162 (interactive)
1163 (when (eq gnus-registry-install 'ask)
1164 (setq gnus-registry-install
1165 (gnus-y-or-n-p
1166 (concat "Enable the Gnus registry? "
1167 "See the variable `gnus-registry-install' "
1168 "to get rid of this query permanently. ")))
1169 (when gnus-registry-install
1170 ;; we just set gnus-registry-install to t, so initialize the registry!
1171 (gnus-registry-initialize)))
1172;;; we could call it here: (customize-variable 'gnus-registry-install)
1173 gnus-registry-install)
1174
8f7abae3 1175;; TODO: a few things
23f87bed
MB
1176
1177(provide 'gnus-registry)
1178
23f87bed 1179;;; gnus-registry.el ends here