;;; gnus-group.el --- group mode commands for Gnus
;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-;; 2005, 2006, 2007 Free Software Foundation, Inc.
+;; 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
;; Keywords: news
;; This file is part of GNU Emacs.
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 3, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING. If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;;; Code:
+;; For Emacs < 22.2.
+(eval-and-compile
+ (unless (fboundp 'declare-function) (defmacro declare-function (&rest r))))
+
(eval-when-compile
- (require 'cl)
- (defvar tool-bar-mode))
+ (require 'cl))
+(defvar tool-bar-mode)
(require 'gnus)
(require 'gnus-start)
:group 'gnus-group-listing
:type '(choice regexp (const nil)))
+(defcustom gnus-safe-html-newsgroups "\\`nnrss[+:]"
+ "Groups in which links in html articles are considered all safe.
+The value may be a regexp matching those groups, a list of group names,
+or nil. This overrides `mm-w3m-safe-url-regexp' (which see). This is
+effective only when emacs-w3m renders html articles, i.e., in the case
+`mm-text-html-renderer' is set to `w3m'."
+ :version "23.2"
+ :group 'gnus-group-various
+ :type '(choice regexp
+ (repeat :tag "List of group names" (string :tag "Group"))
+ (const nil)))
+
(defcustom gnus-list-groups-with-ticked-articles t
"*If non-nil, list groups that have only ticked articles.
If nil, only list groups that have unread articles."
"h" gnus-group-make-help-group
"u" gnus-group-make-useful-group
"a" gnus-group-make-archive-group
- "k" gnus-group-make-kiboze-group
"l" gnus-group-nnimap-edit-acl
"m" gnus-group-make-group
"E" gnus-group-edit-group
"\177" gnus-group-delete-group
[delete] gnus-group-delete-group)
-(gnus-define-keys (gnus-group-soup-map "s" gnus-group-group-map)
- "b" gnus-group-brew-soup
- "w" gnus-soup-save-areas
- "s" gnus-soup-send-replies
- "p" gnus-soup-pack-packet
- "r" nnsoup-pack-replies)
-
(gnus-define-keys (gnus-group-sort-map "S" gnus-group-group-map)
"s" gnus-group-sort-groups
"a" gnus-group-sort-groups-by-alphabet
["Add the archive group" gnus-group-make-archive-group t]
["Make a doc group..." gnus-group-make-doc-group t]
["Make a web group..." gnus-group-make-web-group t]
- ["Make a kiboze group..." gnus-group-make-kiboze-group t]
["Make a virtual group..." gnus-group-make-empty-virtual t]
["Add a group to a virtual..." gnus-group-add-to-virtual t]
["Make an ephemeral group..." gnus-group-read-ephemeral-group t]
(easy-menu-define
gnus-group-misc-menu gnus-group-mode-map ""
`("Gnus"
- ("SOUP"
- ["Pack replies" nnsoup-pack-replies (fboundp 'nnsoup-request-group)]
- ["Send replies" gnus-soup-send-replies
- (fboundp 'gnus-soup-pack-packet)]
- ["Pack packet" gnus-soup-pack-packet (fboundp 'gnus-soup-pack-packet)]
- ["Save areas" gnus-soup-save-areas (fboundp 'gnus-soup-pack-packet)]
- ["Brew SOUP" gnus-group-brew-soup (fboundp 'gnus-soup-pack-packet)])
["Send a mail" gnus-group-mail t]
["Send a message (mail or news)" gnus-group-post-news t]
["Create a local message" gnus-group-news t]
["Browse foreign server..." gnus-group-browse-foreign-server t]
["Enter server buffer" gnus-group-enter-server-mode t]
["Expire all expirable articles" gnus-group-expire-all-groups t]
- ["Generate any kiboze groups" nnkiboze-generate-groups t]
["Gnus version" gnus-version t]
["Save .newsrc files" gnus-group-save-newsrc t]
["Suspend Gnus" gnus-group-suspend t]
(const :tag "Retro look" gnus-group-tool-bar-retro)
(repeat :tag "User defined list" gmm-tool-bar-item)
(symbol))
- :version "23.0" ;; No Gnus
+ :version "23.1" ;; No Gnus
:initialize 'custom-initialize-default
:set 'gnus-group-tool-bar-update
:group 'gnus-group)
See `gmm-tool-bar-from-list' for the format of the list."
:type '(repeat gmm-tool-bar-item)
- :version "23.0" ;; No Gnus
+ :version "23.1" ;; No Gnus
:initialize 'custom-initialize-default
:set 'gnus-group-tool-bar-update
:group 'gnus-group)
See `gmm-tool-bar-from-list' for the format of the list."
:type '(repeat gmm-tool-bar-item)
- :version "23.0" ;; No Gnus
+ :version "23.1" ;; No Gnus
:initialize 'custom-initialize-default
:set 'gnus-group-tool-bar-update
:group 'gnus-group)
See `gmm-tool-bar-from-list' for the format of the list."
:type 'gmm-tool-bar-zap-list
- :version "23.0" ;; No Gnus
+ :version "23.1" ;; No Gnus
:initialize 'custom-initialize-default
:set 'gnus-group-tool-bar-update
:group 'gnus-group)
(defvar image-load-path)
+(defvar tool-bar-map)
(defun gnus-group-make-tool-bar (&optional force)
"Make a group mode tool bar from `gnus-group-tool-bar'.
(goto-char (point-min))
(setq gnus-group-mark-positions
(list (cons 'process (and (search-forward
- (mm-string-as-multibyte "\200") nil t)
- (- (point) 2))))))))
+ (mm-string-to-multibyte "\200") nil t)
+ (- (point) (point-min) 1))))))))
(defun gnus-mouse-pick-group (e)
"Enter the group under the mouse pointer."
(setq not-in-list (delete group not-in-list)))
(when (gnus-group-prepare-logic
group
- (and unread ; This group might be unchecked
+ (and (or unread ; This group might be unchecked
+ predicate) ; Check if this group should be listed
(or (not (stringp regexp))
(string-match regexp group))
(<= (setq clevel (gnus-info-level info)) level)
(if (eq unread t) ; Unactivated?
gnus-group-list-inactive-groups
; We list unactivated
- (> unread 0))
+ (and (numberp unread) (> unread 0)))
; We list groups with unread articles
(and gnus-list-groups-with-ticked-articles
(cdr (assq 'tick (gnus-info-marks info))))
(ticked (gnus-range-length (cdr (assq 'tick marked))))
(group-age (gnus-group-timestamp-delta group))
(inhibit-read-only t))
+ ;; FIXME: http://thread.gmane.org/gmane.emacs.gnus.general/65451/focus=65465
+ ;; ======================================================================
+ ;; From: Richard Stallman
+ ;; Subject: Re: Rewriting gnus-group-highlight-line (was: [...])
+ ;; Cc: ding@gnus.org
+ ;; Date: Sat, 27 Oct 2007 19:41:20 -0400
+ ;; Message-ID: <E1IlvHM-0006TS-7t@fencepost.gnu.org>
+ ;;
+ ;; [...]
+ ;; The kludge is that the alist elements contain expressions that refer
+ ;; to local variables with short names. Perhaps write your own tiny
+ ;; evaluator that handles just `and', `or', and numeric comparisons
+ ;; and just a few specific variables.
+ ;; ======================================================================
+ ;;
+ ;; Similar for other evaluated variables. Grep for risky-local-variable
+ ;; to find them! -- rsteib
+ ;;
;; Eval the cars of the lists until we find a match.
(while (and list
(not (eval (caar list))))
(defun gnus-group-read-group (&optional all no-article group select-articles)
"Read news in this newsgroup.
If the prefix argument ALL is non-nil, already read articles become
-readable. IF ALL is a number, fetch this number of articles. If the
-optional argument NO-ARTICLE is non-nil, no article will be
-auto-selected upon group entry. If GROUP is non-nil, fetch that
-group."
+readable.
+
+If ALL is a positive number, fetch this number of the latest
+articles in the group. If ALL is a negative number, fetch this
+number of the earliest articles in the group.
+
+If the optional argument NO-ARTICLE is non-nil, no article will
+be auto-selected upon group entry. If GROUP is non-nil, fetch
+that group."
(interactive "P")
(let ((no-display (eq all 0))
(group (or group (gnus-group-group-name)))
The arguments are the same as `completing-read' except that COLLECTION
and HIST default to `gnus-active-hashtb' and `gnus-group-history'
respectively if they are omitted."
- (let (group)
+ (let ((completion-styles (and (boundp 'completion-styles)
+ completion-styles))
+ group)
+ (push 'substring completion-styles)
(mapatoms (lambda (symbol)
(setq group (symbol-name symbol))
(set (intern (if (string-match "[^\000-\177]" group)
(message "Quit reading the ephemeral group")
nil)))))
+(defcustom gnus-gmane-group-download-format
+ "http://download.gmane.org/%s/%s/%s"
+ "URL for downloading mbox files.
+It must contain three \"%s\". They correspond to the group, the
+minimal and maximal article numbers, respectively."
+ :group 'gnus-group-foreign
+ :version "23.1" ;; No Gnus
+ :type 'string)
+
+(autoload 'url-insert-file-contents "url-handlers")
+;; FIXME:
+;; - Add documentation, menu, key bindings, ...
+
+(defun gnus-read-ephemeral-gmane-group (group start &optional range)
+ "Read articles from Gmane group GROUP as an ephemeral group.
+START is the first article. RANGE specifies how many articles
+are fetched. The articles are downloaded via HTTP using the URL
+specified by `gnus-gmane-group-download-format'."
+ ;; See <http://gmane.org/export.php> for more information.
+ (interactive
+ (list
+ (gnus-group-completing-read "Gmane group: ")
+ (read-number "Start article number: ")
+ (read-number "How many articles: ")))
+ (unless range (setq range 500))
+ (when (< range 1)
+ (error "Invalid range: %s" range))
+ (let ((tmpfile (mm-make-temp-file
+ (format "%s.start-%s.range-%s." group start range)))
+ (gnus-thread-sort-functions '(gnus-thread-sort-by-number)))
+ (with-temp-file tmpfile
+ (url-insert-file-contents
+ (format gnus-gmane-group-download-format
+ group start (+ start range)))
+ (write-region (point-min) (point-max) tmpfile)
+ (gnus-group-read-ephemeral-group
+ (format "%s.start-%s.range-%s" group start range)
+ `(nndoc ,tmpfile
+ (nndoc-article-type mbox))))
+ (delete-file tmpfile)))
+
+(defun gnus-read-ephemeral-gmane-group-url (url)
+ "Create an ephemeral Gmane group from URL.
+
+Valid input formats include:
+\"http://thread.gmane.org/gmane.foo.bar/12300/focus=12399\",
+\"http://thread.gmane.org/gmane.foo.bar/12345/\",
+\"http://article.gmane.org/gmane.foo.bar/12345/\",
+\"http://news.gmane.org/group/gmane.foo.bar/thread=12345\""
+ ;; - Feel free to add other useful Gmane URLs here! Maybe the URLs should
+ ;; be customizable?
+ ;; - The URLs should be added to `gnus-button-alist'. Probably we should
+ ;; prompt the user to decide: "View via `browse-url' or in Gnus? "
+ ;; (`gnus-read-ephemeral-gmane-group-url')
+ (interactive
+ (list (gnus-group-completing-read "Gmane URL: ")))
+ (let (group start range)
+ (cond
+ ;; URLs providing `group', `start' and `range':
+ ((string-match
+ ;; http://thread.gmane.org/gmane.emacs.devel/86326/focus=86525
+ "^http://thread\.gmane\.org/\\([^/]+\\)/\\([0-9]+\\)/focus=\\([0-9]+\\)$"
+ url)
+ (setq group (match-string 1 url)
+ start (string-to-number (match-string 2 url))
+ ;; Ensure that `range' is large enough to ensure focus article is
+ ;; included.
+ range (- (string-to-number (match-string 3 url))
+ start -1)))
+ ;; URLs providing `group' and `start':
+ ((or (string-match
+ ;; http://article.gmane.org/gmane.comp.gnu.make.bugs/3584
+ "^http://\\(?:thread\\|article\\|permalink\\)\.gmane\.org/\\([^/]+\\)/\\([0-9]+\\)"
+ url)
+ (string-match
+ ;; Don't advertise these in the doc string yet:
+ "^\\(?:nntp\\|news\\)://news\.gmane\.org/\\([^/]+\\)/\\([0-9]+\\)"
+ url)
+ (string-match
+ ;; http://news.gmane.org/group/gmane.emacs.gnus.general/thread=65099/force_load=t
+ "^http://news\.gmane\.org/group/\\([^/]+\\)/thread=\\([0-9]+\\)"
+ url))
+ (setq group (match-string 1 url)
+ start (string-to-number (match-string 2 url))))
+ (t
+ (error "Can't parse URL %s" url)))
+ (gnus-read-ephemeral-gmane-group group start range)))
+
+(defcustom gnus-bug-group-download-format-alist
+ '((emacs . "http://debbugs.gnu.org/%s;mbox=yes")
+ (debian
+ . "http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=%s&mbox=yes"))
+ "Alist of symbols for bug trackers and the corresponding URL format string.
+The URL format string must contain a single \"%s\", specifying
+the bug number, and browsing the URL must return mbox output."
+ :group 'gnus-group-foreign
+ :version "23.2" ;; No Gnus
+ :type '(repeat (cons (symbol) (string :tag "URL format string"))))
+
+(defun gnus-read-ephemeral-bug-group (number mbox-url)
+ "Browse bug NUMBER as ephemeral group."
+ (interactive (list (read-string "Enter bug number: "
+ (thing-at-point 'word) nil)
+ ;; FIXME: Add completing-read from
+ ;; `gnus-emacs-bug-group-download-format' ...
+ (cdr (assoc 'emacs gnus-bug-group-download-format-alist))))
+ (when (stringp number)
+ (setq number (string-to-number number)))
+ (let ((tmpfile (mm-make-temp-file "gnus-temp-group-")))
+ (with-temp-file tmpfile
+ (url-insert-file-contents (format mbox-url number))
+ (write-region (point-min) (point-max) tmpfile)
+ (gnus-group-read-ephemeral-group
+ "gnus-read-ephemeral-bug"
+ `(nndoc ,tmpfile
+ (nndoc-article-type mbox))))
+ (delete-file tmpfile)))
+
+(defun gnus-read-ephemeral-debian-bug-group (number)
+ "Browse Debian bug NUMBER as ephemeral group."
+ (interactive (list (read-string "Enter bug number: "
+ (thing-at-point 'word) nil)))
+ (gnus-read-ephemeral-bug-group
+ number
+ (cdr (assoc 'debian gnus-bug-group-download-format-alist))))
+
+(defun gnus-read-ephemeral-emacs-bug-group (number)
+ "Browse Emacs bug NUMBER as ephemeral group."
+ (interactive (list (read-string "Enter bug number: "
+ (thing-at-point 'word) nil)))
+ (gnus-read-ephemeral-bug-group
+ number
+ (cdr (assoc 'emacs gnus-bug-group-download-format-alist))))
+
(defun gnus-group-jump-to-group (group &optional prompt)
"Jump to newsgroup GROUP.
(let ((entry (assoc (completing-read "Create group: " gnus-useful-groups
nil t)
gnus-useful-groups)))
- (list (cadr entry) (caddr entry))))
+ (list (cadr entry)
+ ;; Don't use `caddr' here since macros within the `interactive'
+ ;; form won't be expanded.
+ (car (cddr entry)))))
(setq method (gnus-copy-sequence method))
(let (entry)
(while (setq entry (memq (assq 'eval method) method))
(cons (current-buffer)
(if (eq major-mode 'gnus-summary-mode) 'summary 'group))))))
+(defvar nnrss-group-alist)
(eval-when-compile
- (defvar nnrss-group-alist)
(defun nnrss-discover-feed (arg))
(defun nnrss-save-server-data (arg)))
(defun gnus-group-make-rss-group (&optional url)
(let* ((title (gnus-newsgroup-savable-name
(read-from-minibuffer "Title: "
(gnus-newsgroup-savable-name
- (or (cdr (assoc 'title
- feedinfo))
- "")))))
+ (mapconcat
+ 'identity
+ (split-string
+ (or (cdr (assoc 'title
+ feedinfo))
+ ""))
+ " ")))))
(desc (read-from-minibuffer "Description: "
- (cdr (assoc 'description
- feedinfo))))
+ (mapconcat
+ 'identity
+ (split-string
+ (or (cdr (assoc 'description
+ feedinfo))
+ ""))
+ " ")))
(href (cdr (assoc 'href feedinfo)))
(coding (gnus-group-name-charset '(nnrss "") title)))
(when coding
(nnrss-save-server-data nil))
(error "No feeds found for %s" url))))
-(defvar nnwarchive-type-definition)
-(defvar gnus-group-warchive-type-history nil)
-(defvar gnus-group-warchive-login-history nil)
-(defvar gnus-group-warchive-address-history nil)
-
-(defun gnus-group-make-warchive-group ()
- "Create a nnwarchive group."
- (interactive)
- (require 'nnwarchive)
- (let* ((group (gnus-read-group "Group name: "))
- (default-type (or (car gnus-group-warchive-type-history)
- (symbol-name (caar nnwarchive-type-definition))))
- (type
- (gnus-string-or
- (completing-read
- (format "Warchive type (default %s): " default-type)
- (mapcar (lambda (elem) (list (symbol-name (car elem))))
- nnwarchive-type-definition)
- nil t nil 'gnus-group-warchive-type-history)
- default-type))
- (address (read-string "Warchive address: "
- nil 'gnus-group-warchive-address-history))
- (default-login (or (car gnus-group-warchive-login-history)
- user-mail-address))
- (login
- (gnus-string-or
- (read-string
- (format "Warchive login (default %s): " user-mail-address)
- default-login 'gnus-group-warchive-login-history)
- user-mail-address))
- (method
- `(nnwarchive ,address
- (nnwarchive-type ,(intern type))
- (nnwarchive-login ,login))))
- (gnus-group-make-group group method)))
-
(defun gnus-group-make-archive-group (&optional all)
"Create the (ding) Gnus archive group of the most recent articles.
Given a prefix, create a full group."
(gnus-group-real-name group)
(list 'nndir (gnus-group-real-name group) (list 'nndir-directory dir)))))
-(defvar nnkiboze-score-file)
-(defun gnus-group-make-kiboze-group (group address scores)
- "Create an nnkiboze group.
-The user will be prompted for a name, a regexp to match groups, and
-score file entries for articles to include in the group."
- (interactive
- (list
- (read-string "nnkiboze group name: ")
- (read-string "Source groups (regexp): ")
- (let ((headers (mapcar 'list
- '("subject" "from" "number" "date" "message-id"
- "references" "chars" "lines" "xref"
- "followup" "all" "body" "head")))
- scores header regexp regexps)
- (while (not (equal "" (setq header (completing-read
- "Match on header: " headers nil t))))
- (setq regexps nil)
- (while (not (equal "" (setq regexp (read-string
- (format "Match on %s (regexp): "
- header)))))
- (push (list regexp nil nil 'r) regexps))
- (push (cons header regexps) scores))
- scores)))
- (gnus-group-make-group group "nnkiboze" address)
- (let* ((nnkiboze-current-group group)
- (score-file (car (nnkiboze-score-file "")))
- (score-dir (file-name-directory score-file)))
- (unless (file-exists-p score-dir)
- (make-directory score-dir))
- (with-temp-file score-file
- (let (emacs-lisp-mode-hook)
- (gnus-pp scores)))))
-
(defun gnus-group-add-to-virtual (n vgroup)
"Add the current group to a virtual group."
(interactive
'summary 'group)))
(error "Couldn't enter %s" dir))))
-(eval-and-compile
- (autoload 'nnimap-expunge "nnimap")
- (autoload 'nnimap-acl-get "nnimap")
- (autoload 'nnimap-acl-edit "nnimap"))
+(autoload 'nnimap-expunge "nnimap")
+(autoload 'nnimap-acl-get "nnimap")
+(autoload 'nnimap-acl-edit "nnimap")
(defun gnus-group-nnimap-expunge (group)
"Expunge deleted articles in current nnimap GROUP."
(provide 'gnus-group)
-;;; arch-tag: 2eb5440f-0bca-4091-814c-e37817536af6
;;; gnus-group.el ends here