/
wpmail.el
362 lines (299 loc) · 13 KB
/
wpmail.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
;;; wpmail.el --- Post to wordpress by e-mail
;; Copyright (C) 2009 Thomas Kappler
;; Author: Thomas Kappler <tkappler@gmail.com>
;; Created: 2009 June 21
;; Keywords: comm, mail, wordpress, blog, blogging
;; URL: <http://github.com/thomas11/wpmail/tree/master>
;; This file is not part of GNU Emacs.
;; This program 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 of the License, or
;; (at your option) any later version.
;; This program 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
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; An Emacs extension to make posting by e-mail to the wordpress.com
;; blog hosting service <http://www.wordpress.com> easier. It might
;; work with other wordpress installations, which I have not tried.
;; For more information about posting to wordpress by e-mail see the
;; support page <http://support.wordpress.com/post-by-email/>.
;; Documentation is a bit lacking, but here's the gist: start a new
;; post, possibly from the region or the buffer, with wpmail-new-post
;; or wpmail-new-post-here. Send it with wpmail-send-post when you are
;; done. wpmail will prompt for title and category; it will propose
;; some titles that you can see via M-n, and it auto-completes the
;; categories in wpmail-categories. See the documentation of these
;; functions for details.
;; You can write your posts in Markdown format
;; <http://daringfireball.net/projects/markdown/> if you have
;; markdown-mode <http://jblevins.org/projects/markdown-mode/>
;; installed. Set wpmail-markdown-command to your Markdown converter
;; and posts will be converted to HTML when sending them.
;;; Dependencies:
;; Message from Gnus. It is included in Emacs, at least in version
;; 23. Tested with Emacs 23 and Gnus v5.13.
;;; Installation:
;; Customize the variables at the top of the code section, and
;; (require 'wpmail) in your init file.
;;; History:
;; 2009-07: First release.
;; 2009-11-03: Add post-configured-p and use it. Allow creating a new
;; post in current buffer.
;; 2009-11-24: Add Markdown support.
;;; TODO
;; When proposing the file name for a title, remove suffixes.
;; Offer before- and after-send hooks, to allow things like
;; transforming the markup or saving all published posts in a certain
;; directory.
;; If you set wpmail-markdown-command, wpmail blindly assumes you use
;; Markdown for all your posts and will convert them all when sending
;; them off.
;;; Code:
(require 'message)
(defconst wpmail-posts-dir "~/Writing/Blog/jugglingbits.wordpress.com/posts"
"The directory where you store your blog posts.
wpmail-new-post will open a new buffer visiting a file there.
Can be nil; you can always turn the current buffer into a blog
post with wpmail-new-post-here, and there is no need to save it
to a file.")
(defconst wpmail-post-email "FOO@post.wordpress.com"
"The e-mail address you got from wordpress.com to send posts to.")
(defvar wpmail-categories '("Academia"
"Best Practices"
"Elsewhere"
"Links"
"Musings"
"Nitty Gritty"
"Own Code"
"Stuff"
"Theory")
"A list of the categories you use for blog posts.
When starting a new post, wpmail will ask you for the category.
These will be available for tab completion. You can also give a
category that is not in this list, but your wordpress must know
it.")
(defvar wpmail-default-tags "programming"
"A list of post tags that will appear whenever you start a new post.")
(defvar wpmail-markdown-command "Markdown.pl")
(defvar wpmail-category-is-also-tag t
"Non-nil means that initially a post's category will also be one of its tags.")
(defconst wpmail-cutoff-line
"-- wpmail markdown cut-off, do not touch --"
"A special line that marks the end of the actual post and the
beginning of the wordpress shortcodes. Will not appear in
posts.")
;; Some helpers that might go into a more general library.
;; -------------------------------------------------------
(defun wpmail-trim (string)
"Remove leading and trailing whitespace from STRING.
From http://www.math.umd.edu/~halbert/dotemacs.html."
(replace-regexp-in-string "\\(^[ \t\n]*\\|[ \t\n]*$\\)" "" string))
(defun wpmail-possible-titles ()
"Make a list of suggestions for a blog post title.
The list contains at least the buffer name. It also contains
some text around point, if it's not empty and not too long."
(defun sensible-option-p (str)
(and (stringp str)
(< (length str) 60)
(> (length (wpmail-trim str)) 4)))
(let ((options (list (buffer-name))))
;; Things at point
(dolist (thing-kind (list 'word 'line 'sentence))
(let ((option (thing-at-point thing-kind)))
(if (sensible-option-p option)
(add-to-list 'options (wpmail-trim option)))))
(delete-dups options)))
(defun wpmail-buffer-or-region ()
"Return the region if it exists, the whole buffer otherwise."
(if (use-region-p)
(buffer-substring (region-beginning) (region-end))
(buffer-substring (point-min) (point-max))))
;; End helpers ---------------------------------------------
(defun wpmail-new-post (title category init-content)
"Start a new wordpress blog post in a new buffer.
The post will have the title TITLE and be in category CATEGORY.
The function proposes some titles based on the buffer name and
text around point, if any. These propositions are in the
\"future history\", accessible by M-n.
In the category prompt, the values of wpmail-categories are
available for auto-completion. You can also enter any category
that is not in wpmail-categories, but your wordpress must know
it.
A new buffer will be created, visiting the file TITLE.wp in
wpmail-posts-dir. There is no need to save this file, however.
You can send it, with TITLE preserved, without saving it.
If INIT-CONTENT is non-nil (interactively, with prefix argument),
the new post buffer is filled with the region if it exists, and
with the whole content of the current buffer otherwise.
The new post buffer will contain a list of shortcodes, directives
the wordpress software evaluates when it receives the post. They
will be initialized to hopefully sensible values, but you should
check them before sending. In particular, you might wish to
change the post tags or the status. See
<http://support.wordpress.com/post-by-email/> for documentation
about shortcodes."
(interactive (list
(read-string "Title: " nil nil (wpmail-possible-titles) nil)
(completing-read "Category: " wpmail-categories)
current-prefix-arg))
(let ((content (if init-content (wpmail-buffer-or-region) nil)))
(wpmail-initialize-new-file title category content)))
(defun wpmail-new-post-here (title category)
"Start a new wordpress blog post in the current buffer.
It works like wpmail-new-post, except that everything happens in
the current buffer."
(interactive (list
(read-string "Title: " nil nil (wpmail-possible-titles) nil)
(completing-read "Category: " wpmail-categories)))
(wpmail-initialize-this-buffer title category (point)))
(defun wpmail-initialize-new-file (title category content)
"Does the actual work after wpmail-new-post got the user's input."
(unless content (setq content ""))
(wpmail-create-and-show-new-post-buffer title category content)
(set-visited-file-name (wpmail-path-to-post-file title)))
(defun wpmail-path-to-post-file (title)
"Find the path to a file with blog post TITLE.
The file will be in wpmail-posts-dir if non-nil, in the current
directory otherwise. If you write in Markdown, the suffix will be
.wp.md; otherwise just .wp."
(let ((dir (if wpmail-posts-dir wpmail-posts-dir ".")))
(concat dir "/" title ".wp" (when wpmail-markdown-command ".md"))))
(defun wpmail-create-and-show-new-post-buffer (title category content)
"Create a new buffer named TITLE and initialize it."
(let ((post-buffer (get-buffer-create title)))
(set-buffer post-buffer)
(wpmail-initialize-this-buffer title category (point-min))
(switch-to-buffer post-buffer)))
(defun wpmail-initialize-this-buffer (title category restore-point)
(let ((configured (wpmail-post-configured-p))
(warning "This buffer seems to be initialized as a wordpress post already. New shortcodes will simply be added at the end. Continue?"))
(when (or (not configured)
(y-or-n-p warning))
(set (make-local-variable 'wpmail-post-title) title)
(goto-char (point-max))
(insert "\n\n"
(wpmail-initial-shortcodes category wpmail-default-tags))
(goto-char restore-point))))
(defun wpmail-initial-shortcodes (category tags)
"Return the wordpress shortcodes as a string; see wpmail-new-post."
(mapconcat 'identity
(list
(when wpmail-markdown-command wpmail-cutoff-line)
(concat "[category " category "]")
(concat "[tags " tags
(if wpmail-category-is-also-tag (concat "," category) "")
"]")
"[status draft]"
"-- "
"Anything after the signature line \"-- \" will not appear in the post."
"Status can be publish, pending, or draft."
"[slug some-url-name]"
"[excerpt]some excerpt[/excerpt]"
"[delay +1 hour]"
"[comments on | off]"
"[password secret-password]")
"\n"))
(defun wpmail-send-post ()
"Send the post to wordpress.com by e-mail.
Partly copied from Trey Jackson
<http://stackoverflow.com/questions/679275/sending-email-in-emacs-programs>."
(interactive)
(let ((configured (wpmail-post-configured-p))
(warning "This post doesn't seem to be configured yet; it lacks either a title or some wordpress shortcodes. (Initialize with wpmail-new-post-here.) Continue?"))
(when (or configured (y-or-n-p warning))
(let* ((buffer-content
(buffer-substring-no-properties (point-min) (point-max)))
(msg-body
(if wpmail-markdown-command
(wpmail-markdown-to-html buffer-content)
buffer-content)))
(message-mail wpmail-post-email wpmail-post-title)
(message-goto-body)
(insert msg-body)
(message-send-and-exit)))))
(defun wpmail-markdown-to-html (post)
"Convert the current post from Markdown to HTML.
Returns the HTML in a string, the current buffer is not modified."
(with-temp-buffer
(insert post)
(goto-char (point-min))
(let ((end-of-post (wpmail-end-of-post)))
(shell-command-on-region (point-min) end-of-post
wpmail-markdown-command t t)
;; Wordpress turns some line breaks into <br />. Grrr.
(let ((fill-column 1000))
(fill-region (point-min) end-of-post))
(buffer-substring-no-properties (point-min) (point-max)))))
(defun wpmail-end-of-post ()
"Find the end of the actual post, i.e., before the wordpress
shortcodes begin."
(save-excursion
(goto-char (point-min))
(search-forward wpmail-cutoff-line nil t)
(beginning-of-line)
(kill-line)
(point)))
(defun wpmail-post-configured-p ()
"Determine whether we're ready to send the current buffer."
(and (boundp 'wpmail-post-title)
(save-excursion
(goto-char (point-min))
(search-forward "[status " nil t))))
;; Unit tests, using el-expectations by rubikitch,
;; <http://www.emacswiki.org/emacs/EmacsLispExpectations>.
;; ---------------------------------------------------------
(eval-when-compile
(when (fboundp 'expectations)
(expectations
;; helpers
(desc "trim")
(expect "foo"
(wpmail-trim "foo"))
(expect "foo"
(wpmail-trim "foo "))
(expect "foo"
(wpmail-trim " foo "))
(expect "foo bar"
(wpmail-trim " foo bar "))
; That'd be nice, but doesn't work with el-expectations.
; (dolist foo '("foo" " foo" "foo " " foo ")
; (expect "foo" (wpmail-trim foo)))
(desc "possible-titles contains buffer name")
(expect (non-nil)
(memq (buffer-name) (wpmail-possible-titles)))
;; wpmail
(desc "post-configured-p")
(expect nil
(with-temp-buffer
(wpmail-post-configured-p)))
(expect nil
(with-temp-buffer
(insert "[status draft]")
(wpmail-post-configured-p)))
(expect (non-nil)
(with-temp-buffer
(set (make-local-variable 'wpmail-post-title) "title")
(insert "[status draft]")
(wpmail-post-configured-p)))
(desc "initialize-this-buffer")
(expect (non-nil)
(with-temp-buffer
(wpmail-initialize-this-buffer "title" "category" (point-min))
(wpmail-post-configured-p)))
(desc "end of post")
(expect 1
(with-temp-buffer
(insert wpmail-cutoff-line)
(wpmail-end-of-post)))
(expect 10
(with-temp-buffer
(insert "bla bla\n\n")
(insert wpmail-cutoff-line)
(wpmail-end-of-post))))))
;; End unit tests. -----------------------------------------
(provide 'wpmail)
;;; wpmail.el ends here