How to automatically track links in org-mode to gnus messages? - emacs

I use org-mode + gnus + Gmail for my daily GTD routine. The concept is that treating all incoming messages as tasks, and converting all messages in INBOX into org-mode's tasks using org-capture. Once all new messages are converted into tasks, archive them, and hopefully INBOX is kept zero.
My workflow is as follows:
Open the summary view of gnus INBOX, and select a new message
Capture the message with org-store-link (C-c l)
Open my todo file (todo.org), and create a new task for it, and paste the captured link to the task's body with org-insert-link (C-c C-l)
Go back to gnus summary view and archive the message (B del)
The problem is that when moving a message into the archive folder, the captured link becomes broken, and I cannot follow the link anymore. This is because the captured link includes the IMAP folders' name and archiving message changes the message's IMAP folder name. E.g.,
Captured link: [[gnus:nnimap%2Blocalhost:%5BGmail%5D.Important#1364607772002.9702fb8c#Nodemailer][Email from Geeklist Team: Geekli.st Suggestions & Activi]] (IMAP folder name is "[Gmail]Important")
Link to archived message: [[gnus:nnimap%2Blocalhost:%5BGmail%5D.All Mail#1364607772002.9702fb8c#Nodemailer][Email from Geeklist Team: Geekli.st Suggestions & Activi]] (IMAP folder name is "[Gmail]All Mail")
So my question is: how can I update the captured link automatically when the message is moved to other folders? I guess there are some hooks to do this, but I could not find a good sample for this purpose. Or any simpler solutions for this kind of routine are welcome. TIA.

I do not use 'org-store-link' and 'org-insert-link' but a capture template, that automatically generates a link to the message (%a below). So you do not have to switch buffers to store a TODO entry:
(setq org-capture-templates
'(
("m" "TODO from Mail" entry (file+headline "~/gitfiles/org/gtd.org" "Inbox")
"* TODO %?, Link: %a")))
Since all my emails arrive in the INBOX and are archived in the folder "Archive" I can just use the following function which replaces the string 'INBOX' by 'Archive' in the Org mode link in the capture buffer:
(defun hs/replace ()
(interactive)
(goto-char 1)
(replace-string "INBOX" "Archive"))
This hook calls the function when I hit C-c C-c to file the capture entry:
(add-hook 'org-capture-prepare-finalize-hook 'hs/replace)
So, my workflow is as follows:
Select a message in Summary buffer
Hit C-c c m to capture a TODO item with Link to message and write a description (since the message is still in the inbox, the generated link contains the group "INBOX")
Hit C-c C-c to file the TODO entry (this calls the function 'hs/replace' which replaces the string INBOX by Archive)
Archive the email in the archive folder.
HTH

Related

Org Capture: prompt for information when determining the target file

I'm looking to build a capture template that, when run, prompts the user for more information to determine the path of the target file.
I have a few pieces already.
A function which asks the user for data and returns a string:
(defun my/get-121-orgfile ()
"Ask the user for the name of the participant so that we can build"
(interactive)
(read-string "Participant Name: ")
)
An org-capture-template which will run the prompt successfully when emacs loads:
(setq org-capture-templates
`(
("m1" "1-1 meetings")
("m1b" "prep for a 1-1 meeting" entry
(file ,(concat "~/org/meetings/1-2-1/" (my/get-121-orgfile) ".org"))
(file "~/org/templates/meeting-121-prep.org")
:clock-in t)
))
I took the back quote and comma pattern from this SO answer, but I haven't been able to figure out how to scope this behaviour to when I select the template: I want the prompt to pop up each time I hit <org-capture>m1b.
The backquote-comma pattern will not help here: it will call the function at the time that org-capture-template is set. What you are trying to do is to call the function when the capture is executed.
The only way I know to do that would to use the (function foo) target mechanism of org-capture-templates. The doc string says:
(function function-finding-location)
Most general way: write your own function which both visits
the file and moves point to the right location
So you would not be able to use the (file ...) target as you do above. Instead you have to write a function that gets all the information you want and then visit the target file at the target location and add the filled-out template.
This is not a complete answer but it was too long for a comment, but maybe it helps to point you in the right direction.

Grabbing subheaders in org-mode capture template.

I'm using org-mode to write some prose. I'm using the org-capture templates as a convenient way to make notes about edits I need to make later. As I am writing, I can quickly capture an idea or make a note about something that needs to be researched later, and then continue writing.
The problem is that the capture template is not grabbing subheadings, so I need to search the entire text to determine where the edit should be.
Here is the current capture template:
("h" "Writing TODO" entry (file+headline "~/Book-Outline.org" "EDITS")
"* TODO - %?\n \%i\n \%a")
The more specific the capture information, the easier it will be to go back to the exact location in the book to make the edits. Additional ideas or suggestions are appreciated if the capture templates will not do this.
The solution I use is something like:
("h" "Writing TODO" entry (file+headline "~/Book-Outline.org" "EDITS")
"* TODO - %?\n %i\n[[%l][In file %f]]")
It creates an org link to the file when org-capture was called
From Emacs doc here
%l: like %a, but only insert the literal link.
%f: file visited by current buffer when org-capture was called.
Another possible explanation:
In my Org templates I do not use \, maybe you can try to replace your \%a by %a, without the \. (I have not checked that, sorry).
Maybe i'm not getting your point, but normally you should start the capture and in the capture buffer, you can press C-c C-w to refile the capture, then navigate through your org-file until you reached the desired destination.
Org will save your capture then there.
Is that not what you want?

Force DocView mode to show updated file without confirmation?

I use DocView Mode to display .pdf compilations via "latex-preview-pane-mode." Recently, Emacs will ask me "file ____.pdf changed on disk. Reread from disk? (yes or no)".
Typing "yes" each time disrupts my workflow. I have tried setting auto-revert-mode for the DocView buffer, but this did not help. Is there any way to fix this, or any idea why it changed suddenly (no changes to my .emacs.d in the recent past).
To achieve what Tristan suggests, first I tried M-x customize-variable RET revert-without-query, but couldn't get very far, so I wrote this in my init.el file:
(setq revert-without-query '(".pdf"))
and I'm happily udating my pdf files from org-mode without getting queried every time. (I use pdf-tools).
Source: fourth answer to a similar question in stackoverflow
(defun revert-buffer-no-confirm ()
"Revert buffer without confirmation."
(interactive)
(revert-buffer :ignore-auto :noconfirm))
Source: http://www.emacswiki.org/emacs-en/download/misc-cmds.el
Maybe this function could help you out
Take a look at the variable revert-without-query. From the Emacs Lisp documentation:
This variable holds a list of files that should be reverted without
query. The value is a list of regular expressions. If the visited
file name matches one of these regular expressions, and the file
has changed on disk but the buffer is not modified, then
‘revert-buffer’ reverts the file without asking the user for
confirmation.
Adding .+\.pdf to the list should make buffers visiting pdf files revert when you change the file on disk.

Gnus: How to archive emails according to the account they were written from? [gcc-self not working as expected]

I have two mail accounts, foo.bar#uni.edu and foo.bar#gmail.com. I would like to archive messages I send from either one in a corresponding "sent mail" folder (nnimap+foo.bar#uni.edu:Sent Items and foo.bar#gmail.com:[Google Mail]/Sent Mail).
I tried to set
(setq gnus-message-archive-group
'(("uni" "nnimap+foo.bar#uni.edu:Sent Items")
("gmail" "nnimap+foo.bar#gmail.com:[Google Mail]/Sent Mail")
))
but that does not set Gcc (new messages don't have a Gcc; any solution here?). I thus went back to (setq
gnus-message-archive-group "nnimap+foo.bar#uni.edu:Sent Items") which sets Gcc
correctly (for the main account foo.bar#uni.edu) if I open a new message in *Group* via m.
I then tried to use gcc-self via gnus-parameters to archive the sent mails correctly:
(setq gnus-parameters
`((,(rx "nnimap+foo.bar#uni.edu")
(gcc-self . "nnimap+foo.bar#uni.edu:Sent Items"))
(,(rx "nnimap+foo.bar#gmail.com")
(gcc-self . "foo.bar#gmail.com:[Google Mail]/Sent Mail"))))
The manual (http://www.gnus.org/manual/gnus_28.html) says that if gcc-self is
a string, it is simply inserted literally as Gcc header. I made the following
experience: Wherever I start a new message in *Group* via C-u m (with m, Gcc
is "nnimap+foo.bar#uni.edu:Sent Items" as mentioned before), Gcc is taken to be
the name the point was on in *Group* before m was hit. So if the point is on
nnimap+foo.bar#gmail.com:Drafts, Gcc will be Gcc:
nnimap+foo.bar#gmail.com:Drafts (instead of foo.bar#gmail.com:[Google
Mail]/Sent Mail). How can this be fixed and messages archived in the corresponding sent mail folders if written via C-u m? In other words, why are the Gcc's not set correctly?
[this is on Emacs 24.3.50.1, Gnus v5.13]
I had exactly the same issue as you. Even though I was adding in a gcc-self parameter to be "INBOX.Sent" when I sent the message it was ending up in "nnfolder+archive:sent.YYYY-MM"
My setup is that I have a default account (home) and a secondary account (work) both imap (but not Gmail, hopefully this answer still applies)
Through a lot of trial and error I managed to get it functioning as I wanted: work emails to be saved in the work sent folder, home emails to be saved in the home sent folder.
In the gnus-parameters I simply changed my gcc-self param to gcc and it worked! However, only for the secondary address.
For the default address I set gnus-message-archive-group
A cut down of my ~/.gnus file
(setq gnus-select-method
'(nnimap "home"
(nnimap-address "mail.homeaddress.com")
(nnimap-server-port 143)
(nnimap-stream starttls)
(nnimap-inbox "INBOX")
))
(setq gnus-secondary-select-methods
'((nnimap "work"
(nnimap-address "mail.workaddress.com")
(nnimap-server-port 143)
(nnimap-stream starttls)
(nnimap-inbox "INBOX"))))
(setq gnus-parameters
'(
("work"
(posting-style
(address "me#workaddress.com")
(gcc "nnimap+work:INBOX.Sent")))))
(setq gnus-message-archive-group "nnimap:INBOX.Sent")
Notice that I don't have any posting-styles for home.
I hope this helps.
Emacs Version 24.3.1, Gnus v5.13
I have encountered the same problem during my Gnus setup. I use Gmail for personal stuff and Outlook for work. My goal is to compose/reply messages using the corresponding account that I am currently working on in Gnus. Based on the suggestions from robsearles, I managed to achieve this goal using gnus-posting-styles. Here is the sample code I use.
;; Archive outgoing email in Sent folder on imap.gmail.com
(setq gnus-message-archive-method '(nnimap "imap.gmail.com")
gnus-message-archive-group "[Gmail]/Sent Mail")
;; Set return email address based on incoming email address
(setq gnus-posting-styles
`((".*"
(address "foo.bar#gmail.com")
(name "Foo Bar")
("X-Message-SMTP-Method" "smtp smtp.gmail.com 587 foo.bar#gmail.com")
)
("^nnimap[+]outlook:.*"
(address "foo.bar#outlook.com")
(name "Foo Bar")
("X-Message-SMTP-Method" "smtp smtp-mail.outlook.com 587 foo.bar#outlook.com")
(gcc "\"nnimap+outlook:Sent Items\"")
)
)
)
The gnus-message-archive-method and gnus-message-archive-group set the default archiving behavior which archives messages to my Gmail Sent folder. The gcc tag in gnus-posting-styles instructs Gnus to archive messages to my Outlook Sent folder when I am working with the Outlook account. I also get the benefit of automatically selecting the outgoing mail server depending on the email account I am working on with the X-message-SMTP-Method tag. Outlook seems to automatically archives a message to the Sent folder whenever it is sent, so I used (gcc nil) in my actual setup to avoid duplicates. You can of course change outlook to whatever mail service you are using.

How to generate dynamic "Reply-To:" based on "Message-ID:"? [+detail]

How can you generate a dynamic "Reply-To:" (and "From:") header in emacs/gnus based on Message-ID of the created message? I would like to use external (perl) script to generate a dynamic +detail part based on the "Messaged-ID:" header.
user+detail#example.net
I have managed to create a header with content generated by my external script. The script gets usenet group name as command line parameter. I would like to pass it the message-id value too.
My current code
~/.emacs :
'(gnus-posting-styles ("^pl\\.test$" ("Reply-To" message-make-reply-to)))
~/.gnus
(defun message-make-reply-to()
(my-script ".../reply-to.pl" (message-fetch-field "Message-Id")))
(defun my-script(path &optional param) ....
The problem: the script does not receive message-id as its parameter (my-script gets correctly explicitly set parameter)
;; Make sure the Message-ID header is present in newly created messages
(setq message-generate-headers-first '(Message-ID))
;; Prevent emacs from resetting the Message-ID before the message is sent.
(setq message-deletable-headers
(remove 'Message-ID message-deletable-headers))
(setq gnus-posting-styles
'(("^pl\\.test$"
("Reply-To" '(message-make-reply-to)))))
Note the additional quote and parentheses around message-make-reply-to. The explanation for this is that the function is run at different times, depending on whether it's given as a symbol or as a quoted s-expression.
If given as symbol, it is run when a lambda function is added to message-setup-hook. That happens in a message-mode-hook, i.e. right after the new buffer is created and switched into message-mode. The cause for this is some wild quoting/unquoting of values during creation of the lambda function.
If given as a quoted sexpr, evaluation is delayed until after the buffer is filled with initial values. It is close to the last code which is run on message setup.
Alternative Solution (without gnus-posting-styles)
In cases where the new header should be added to every new message, the Reply-To header can also be set using the message-header-setup-hook. A custom hook needs to be defined to add the header for each new message.
(defun reply-to-message-header-setup-hook ()
(let* ((msg-id (message-fetch-field "Message-ID"))
(reply-to (my-script ".../reply-to-pl" msg-id)))
(message-add-header (concat "Reply-To: " reply-to))))
;; Call the hook every time a new message is created
(add-hook 'message-header-setup-hook 'reply-to-message-header-setup-hook)
;; Make sure the Message-ID header is present in newly created messages
(setq message-generate-headers-first '(Message-ID))