1. 为什么用org-mode写博客
我最开始用Emacs, 是因为org-mode。这是一个专注于写,而让我忽略展示结果的一种写作方式。为 什么这么说?因为所有内容的格式都是可定制的。按照自己喜欢的格式编写一些格式化配置, 就可以 把org-mode写的内容输出到拥有特定格式的文件,比如html、pdf,这两种常用的文件类型。
除此外,org-mode还可以做计划(TODO list) 等等。这些对于日常的工作都是非常有帮助的。
我喜欢上了org-mode.
与此同时,我又写技术类博客,把自己总结的内容发布到网络上,以飨读者。而很多时候,为了在博客 上格式化自己的文档,又是一件让我兴奋到五体投地的事情。
我尝试把导出成html文件的内容直接copy进博客,发现有些内容无法copy,或者原本的格式已经面目全非。 然后就研究了下,怎么样能把org-mode的内容直接发布到博客,这样可以保持原有的简约格式。
说白了,最根本的原因就是: 我懒!我懒!我懒!重要的事情说三遍!我不想花太多的时间去对文字图片进行重复格式化
2 说明
org-mode 写博客园的方法,主要 是copy 了huwenbiao GITHUB 的代码, 而他的代码是在GITHUB:hexmode 的基础上 针对cnblogs进行了扩展开发,已经很久很久没有更新了,可能已经不再维护了吧。我按照自己的想法对Open_Source的代码做了一些简单的修改。
修改内容:
- 取消从网站下载博文 原代码,在每次连接cnblogs前,在设置cnblogs的个人信息时,都会像苍蝇一样问我,是不是要把cnblogs里的博文下载下来,下载的博文是以博文ID命名的html内容文件。个人感觉没有太大的意义 。 而每次都得输入: no. 再次声明: 我懒! 所以我把下载博文的代码给删除了。
- 减少输入个人信息次数 每次要发布博文之前,都要手动设置自己的博客信息(blog id/ 登录名/密码)。 就像老婆让我睡觉前一定要洗澡一样烦(不会被老婆看到吧~哈哈), 所以我修改了原代码,将个人信息的相关变量单独配置, 以后每次写完org文件,直接
C-c c p
就可以直接发布,而不用每次都要先输入一次个人信息。而如果 没有配置,修改以后的程序会提醒你输入。 - 更新xml-rpc 因为 "huwenbiao"已经很久没有更新代码,所以我从GITHUB:hexmode 上复制了最新的内容。
主要是这些修改,其他还有一些细节和修复一些BUG。
总体来说,已经能满足我的需求了。
下面提供我修改以后的cnblogs.el
;;;cnblogs.el --- Emacs :Configuration for writing cnblogs with org-mode.
;; Created: Tue Jun 11 00:18:39 2019
;;
;; Copyright © 2019-
;;
;; Author: halberd.lee@gmail.com
;; URL:
;; Version: 20190610
;; Keywords: convenience
;; This file is not part of GNU Emacs.
;;; Commentary:
;; Some Linux specific stuff.
;;; License:
;; 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 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.
;;; Code:
;; On Linux Emacs doesn't use the shell PATH if it's not started from
;; the shell. Let's fix that:
;;todo检查所有的底层函数,正常运行返回t,否则返回nil
;;给所有的操作加上反馈信息
;;; entry type
;;
;; (id title postid categories src-file state)
;; (int string int(string) list string string)
(require 'metaweblog)
(defgroup cnblogs nil
"博客园客户端分组"
:group 'emacs)
;;(defun cnblogs-define-variables ()
"定义及初始化各变量"
(defcustom blog-base-url "https://www.cnblogs.com/"
"blog 域名."
:group 'cnblogs
:type 'string)
(defcustom cnblogs-server-url nil
"MetaWeblog访问地址"
:group 'cnblogs
:type 'string)
(defcustom cnblogs-blog-id "halberd-lee"
"博客ID"
:group 'cnblogs
:type 'string)
(defcustom cnblogs-user-name "halberd.lee"
"登录用户名"
:group 'cnblogs
:type 'string)
(defcustom cnblogs-user-passwd "12345678"
"用户密码."
:group 'cnblogs
:type 'string)
(defcustom cnblogs-media-object-suffix-list '("jpg" "jpeg" "png" "gif" "mp4")
"希望处理的媒体文件类型"
:group 'cnblogs
:type 'list)
(defcustom cnblogs-src-file-extension-list '("org" "html")
"希望处理的媒体文件类型"
:group 'cnblogs
:type 'list)
(defcustom cnblogs-template-head
"#TITLE: #KEYWORDS: #DATE: "
"博客头模板."
:group 'cnblogs
:type 'list)
(defcustom cnblogs-file-root-path "~/wiki/cnblogs/"
"数据文件的根目录."
:group 'cnblogs
:type 'string)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defvar cnblogs-posts-in-category nil
"分类之后的博文,这是显示在Cnblogs-Manager缓冲区里的主要内容.")
(defvar cnblogs-entry-list-file
"博文项列表文件."
(concat cnblogs-file-root-path "entry-list-file"))
(defvar cnblogs-file-post-path
"博文内容文件根目录,其中的博文内容文件以博文id命名."
(expand-file-name "post/" cnblogs-file-root-path ))
(defvar cnblogs-category-list nil
"博文分类列表.")
(defvar cnblogs-category-list-file
"博文分类列表."
(expand-file-name "category-list-file" cnblogs-file-root-path ))
(defvar cnblogs-blog-info nil
"博客信息.")
(defvar cnblogs-entry-list nil
"本地博客列表.")
(defvar cnblogs-category-list nil
"分类列表.")
(defvar cnblogs-post-list-window nil
"博文列表窗口.")
(setq test-post `(("title" . "博文题目")
("dateCreated" :datetime (20423 52590))
("categories" "categories" "[随笔分类]Emacs" "[随笔分类]Linux应用")
("description" . "博文正文。")))
;;;;;;;;;;;;;;;;;;;;;;;;;;Menu;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;"cnblogs博客客户端菜单")
(defvar cnblogs-mode-map
(make-sparse-keymap "Cnblogs"))
;;(define-key cnblogs-mode-map [tags-getUsersBlogs]
;; '(menu-item "User information" cnblogs-get-users-blogs
;; :help "获取用户的博客信息"))
;;
;;(define-key cnblogs-mode-map [tags-getRecentPosts]
;; '(menu-item "Get recent posts" cnblogs-get-recent-posts
;; :help "获取最近发布的N篇博客"))
;;
;;(define-key cnblogs-mode-map [tags-getCategories]
;; '(menu-item "Get(Update) categories" cnblogs-get-categories
;; :help "获取并更新本地博客分类"))
;;
;;(define-key cnblogs-mode-map [tags-getPost]
;; '(menu-item "Get post" cnblogs-get-post
;; :help "获取并更新本地指定的博客"))
;;(define-key cnblogs-mode-map [separator-cnblogs-tags]
;; '(menu-item "--"))
;;
;;(define-key cnblogs-mode-map [tags-editPost]
;; '(menu-item "Update post" cnblogs-edit-post
;; :help "更新已发布的博客"))
;;
;;(define-key cnblogs-mode-map [tags-deletePost]
;; '(menu-item "Delete post" cnblogs-delete-post
;; :help "将当前缓冲区对应的博客删除"))
;;
;;(define-key cnblogs-mode-map [tags-saveDraft]
;; '(menu-item "Save draft" cnblogs-save-draft
;; :help "将草稿保存到服务器,但状态为“未发布”"))
;;
;;(define-key cnblogs-mode-map [tags-newPost]
;; '(menu-item "Publish post" cnblogs-new-post
;; :help "发布当前缓冲区"))
;;
;;(define-key cnblogs-mode-map [C-S-mouse-1]
;; cnblogs-mode-map)
;; )
;(cnblogs-define-variables)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;LoadData;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;(cnblogs-load-entry-list) ;; todo: 放在初始化中
(defun cnblogs-load-entry-list ()
(setq cnblogs-entry-list
(condition-case ()
(with-temp-buffer
(insert-file-contents cnblogs-entry-list-file)
(car (read-from-string (buffer-string))))
(error nil))))
(defun cnblogs-save-entry-list ()
"保存cnblogs-entry-list,成功返回t,否则返回nil"
(condition-case ()
(with-temp-file cnblogs-entry-list-file
(print cnblogs-entry-list
(current-buffer)))
(error nil)))
(defun cnblogs-load-category-list ()
(setq cnblogs-category-list
(condition-case ()
(with-temp-buffer
(insert-file-contents cnblogs-category-list-file)
(car (read-from-string (buffer-string))))
(error nil))))
(defun cnblogs-save-category-list ()
;先将分类格式简化,只留取名字
(setq cnblogs-category-list
(mapcar (lambda (category)
(cdr (assoc "description" category))
)
cnblogs-category-list))
(with-temp-file cnblogs-category-list-file
(print cnblogs-category-list
(current-buffer))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;底层函数;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun cnblogs-check-legal-for-publish (src-file)
"检查文件是否可以发布"
(and
(if (member (file-name-extension src-file)
cnblogs-src-file-extension-list)
t
(progn
(message "Failed: UNSUPPORTED file!")
nil))
(if (equal (cnblogs-check-src-file-state (buffer-file-name))
"PUBLISHED")
(progn
(message "This post has been published, you can update it, using M-x cnblogs-edit-post")
nil)
t)))
(defun cnblogs-check-legal-for-delete (src-file)
"检查文件是否可以删除相应的博文"
(and
(if (member (file-name-extension src-file)
cnblogs-src-file-extension-list)
t
(progn
(message "Failed: UNSUPPORTED file!")
nil))
(if (equal (cnblogs-check-src-file-state (buffer-file-name))
"PUBLISHED")
t
(progn
(message "This post has not been published, so you cann't delete it!")
nil))))
(defun cnblogs-check-legal-for-edit (src-file)
"检查文件是否可以更新"
(and
(if (member (file-name-extension src-file)
cnblogs-src-file-extension-list)
t
(progn
(message "Failed: UNSUPPORTED file!")
nil))
(if (equal (cnblogs-check-src-file-state (buffer-file-name))
"PUBLISHED")
t
(progn
(message "This post has not been published, you can't update it. You can publish it using M-x cnblogs-new-post")
nil))))
(defun cnblogs-check-src-file-state (src-file)
(let ((state nil))
(mapc (lambda (entry)
(if (equal src-file (nth 4 entry))
(setq state (nth 5 entry))))
cnblogs-entry-list)
state))
(defun cnblogs-gen-id ()
"给entry产生一个id,从1开始"
(let ((id 0)
(flag t))
(while flag
(progn
(setq flag nil id (1+ id))
(mapc (lambda (entry)
(and (equal id
(car entry))
(setq flag t)))
cnblogs-entry-list)))
id))
(defun cnblogs-push-post-to-entry-list (post)
"将博文保存到cnblogs-entry-list变量中。但并不立即保存到文件中"
(let ((title (cdr (assoc "title" post)))
(postid (cdr (assoc "postid" post)))
(categories (cdr (assoc "categories" post)))
(done nil)
(index 0))
(progn
(if (integerp postid)
(setq postid (int-to-string postid)))
;;保存博文
(with-temp-file (concat cnblogs-file-post-path postid)
(print post (current-buffer)))
;;如果有相同标题的博文项,则提示是否合并到同一项中去,如果有已经存在多个相同的项,对每个都询问,直到回答是或者完
(mapc (lambda (entry)
(progn
(or done
(not (equal title (nth 1 entry)))
(not (y-or-n-p (format "merge the post %s with entry %S" postid entry)))
;下面是将该博文合并到该项中
(progn
(setq done t)
(setcar (nthcdr index cnblogs-entry-list)
;id
(list (nth 0 entry)
;title
title
;postid
postid
;categories
categories
;src-file
(nth 4 entry)
;state
"PUBLISHED"))))
(setq index (1+ index))))
cnblogs-entry-list)
;还没有插入则新建项
(or done
(push
;id
(list (cnblogs-gen-id)
;title
title
;postid
postid
;categories
categories
;src-file
nil
;state
"PUBLISHED")
cnblogs-entry-list)))))
(defun cnblogs-push-src-file-to-entry-list (src-file)
"将一个源文件加入到博文项中,但并不立即保存博文项到文件中。"
(if (cnblogs-check-file-in-entry-list src-file)
t
(let ((title
(with-temp-buffer
(insert-file-contents src-file)
(cnblogs-fetch-field "TITLE")))
(done nil)
(index 0))
(progn (mapc (lambda (entry)
(progn
(or done
(not title)
(not (equal title (nth 1 entry)))
(not (y-or-n-p (format "merge the file %s with entry %S" src-file entry)))
;下面是将该文件合并到该项中
(progn
(setq done t)
(setcar (nthcdr 4 (nth index cnblogs-entry-list))
src-file)))
(setq index (1+ index))))
cnblogs-entry-list)
;还没有插入则新建项
(or done
(push
;id
(list (cnblogs-gen-id)
;title
title
;postid
nil
;categories
nil
;src-file
src-file
;state
"UNPUBLISHED")
cnblogs-entry-list))))))
(defun cnblogs-assign-post-to-file (post src-file)
"将post合并到一个指定源文件的列表项中,成功返回t,不立即保存列表项"
(condition-case()
(progn
(setq cnblogs-entry-list
(mapcar (lambda (entry)
(if (equal src-file
(nth 4 entry))
;id
(list (nth 0 entry)
;title
(cdr (assoc "title" post))
;postid
(cdr (assoc "postid" post))
;categories
(cdr (assoc "categories" post))
;src-file
src-file
"PUBLISHED")
entry))
cnblogs-entry-list))
t)
(error nil)))
(defun cnblogs-categories-string-to-list (categories-string)
"将分类字符串按空白符分成字符串列表"
(if (or (eq categories-string nil)
(eq categories-string ""))
nil
(let ((idx1
(string-match "[^ ]+" ;圆角半角空格
categories-string)))
(if (not idx1)
nil
(setq categories-string ;圆角半角空格
(substring categories-string idx1))
(let ((idx2
(string-match "[ ]+"
categories-string)))
(if idx2
(cons (concat "[随笔分类]"
(substring categories-string
0
idx2))
(cnblogs-categories-string-to-list (substring categories-string
idx2)))
(cons (concat "[随笔分类]"
categories-string)
nil)))))))
(defun cnblogs-fetch-field (field)
(let* ((regexp
(concat "^[ ]*[#]+[\+]?"
field
":[^ ]*"))
(idx (string-match regexp
(buffer-substring-no-properties (point-min)
(point-max)))))
(if idx
(let* ((field-val (match-string 0
(buffer-substring-no-properties (point-min)
(point-max))))
(val (substring field-val
(1+ (string-match ":" field-val))))
(idx2 (string-match "[^ ]+"
val)))
(and idx2
(substring val
idx2)))
nil)))
(defun cnblogs-make-media-object-file-data (media-path) ;todo: type可能要详细分类
"根据给出的文件路径返回相应的FileData,文件不存在返回nil"
(and (file-exists-p media-path)
(list
;;media-path name
(cons "name"
(file-name-nondirectory media-path))
;; bits
(cons "bits"
(base64-encode-string
(with-temp-buffer
(insert-file-contents-literally media-path)
(buffer-string))))
(cons "type" "image/jpg"))))
(defun cnblogs-org-mode-buffer-to-post ()
(delq nil(list
;; title
(cons "title"
(or (cnblogs-fetch-field "TITLE")
"新随笔"))
;; excerpt
(cons "mt_excerpt"
(or (cnblogs-fetch-field "DESCRIPTION")
""))
;; categories
(cons "categories"
(let ((categories-list
(cnblogs-categories-string-to-list
(cnblogs-fetch-field "CATEGORIES"))))
(or
categories-list
'("[随笔分类]未分类"))))
;; tags
(cons "mt_keywords"
(or
(cnblogs-fetch-field "KEYWORDS")
""))
;; dateCreated
(cons "dateCreated"
(list
:datetime
(condition-case ()
(date-to-time (cnblogs-fetch-field "DATE")) ;todo: 要转化
(error (progn
(message "时间格式不支持,使用默认时间:1989-05-17 00:00")
(date-to-time "1989-05-17 00:00"))))))
;; description
(cons "description"
(with-current-buffer (org-html-export-as-html)
(let ((buf-str
(cnblogs-replace-media-object-location
(buffer-substring-no-properties
(point-min)
(point-max)))))
(kill-buffer)
buf-str))))))
(defun cnblogs-other-mode-buffer-to-post () ;todo: post还不完全
(delq nil
(list
;; title
(cons "title"
(or (cnblogs-fetch-field "TITLE")
"新随笔"))
;; categories
(cons "categories"
(let ((categories-list
(cnblogs-categories-string-to-list
(cnblogs-fetch-field "CATEGORIES"))))
(or
categories-list
'("[随笔分类]未分类"))))
;; tags
(cons "mt_keywords"
(or
(cnblogs-fetch-field "KEYWORDS")
""))
;; dateCreated
(cons "dateCreated"
(list
:datetime
(condition-case ()
(date-to-time (cnblogs-fetch-field "DATE")) ;todo: 要转化
(error (progn
(message "时间格式不支持,使用默认时间:1989-05-17 00:00")
(date-to-time "1989-05-17 00:00"))))))
;; description
(cons "description"
(cnblogs-replace-media-object-location
(buffer-substring-no-properties
(cnblogs-point-template-head-end)
(point-max)))))))
(defun cnblogs-insert-template-head ()
"插入头模板"
(interactive)
(save-excursion
(goto-char (point-min))
(insert cnblogs-template-head)))
(defun cnblogs-delete-entry-from-entry-list (postid)
"通过postid删除博文项及posts目录下相应的文件,POSTID是string类型"
(condition-case ()
(progn
(setq cnblogs-entry-list
(remove-if (lambda (entry)
(equal postid
(nth 2 entry)))
cnblogs-entry-list))
(cnblogs-save-entry-list)
(and (file-exists-p (concat cnblogs-file-post-path postid))
(delete-file (concat cnblogs-file-post-path postid)))
t)
(error nil)))
(defun cnblogs-get-postid-by-title (title)
(and (stringp title)
(let ((postid nil))
(mapc (lambda (entry)
(or postid
(and (equal title
(nth 4 cnblogs-entry-list))
(setq postid
(nth 2 cnblogs-entry-list)))))
cnblogs-entry-list)
(and postid
(integerp postid)
(int-to-string postid))
(or postid
(setq postid "0"))))
postid)
(defun cnblogs-get-postid-by-src-file-name (filename)
"在cnblogs-entry-list中查找src-file为filename的项的博文id,找不到返回"0""
(let ((postid nil))
(mapc (lambda (entry)
(if (equal filename (nth 4 entry))
(setq postid (nth 2 entry))))
cnblogs-entry-list)
(or postid
(setq postid "0"))
postid))
(defun cnblogs-replace-media-object-location (buf-str)
"处理BUF-STR中的媒体文件,返回处理后的字符串"
(mapc (lambda (suffix)
(let ((regexp
(concat "[file]*[:]?[/\]*[a-z]?[:]?[^:*"?<>|#]+."
suffix))
(current 0))
(while (string-match regexp
buf-str
current)
(let* ((media-path (match-string 0
buf-str))
(media-url
(save-match-data
(and (file-exists-p media-path)
(cnblogs-metaweblog-new-media-object
(cnblogs-make-media-object-file-data
media-path))))))
(if media-url
(progn
(setq current
(+ (match-beginning 0)
(length media-url)))
(setq buf-str
(replace-match media-url
t
t
buf-str)))
(setq current
(match-end 0)))))))
cnblogs-media-object-suffix-list)
buf-str)
(defun cnblogs-point-template-head-end ()
(print (save-excursion
(goto-char (point-min))
(forward-paragraph)
(point))))
(defun cnblogs-current-buffer-to-post ()
(cond
((equal mode-name
"Org")
(cnblogs-org-mode-buffer-to-post))
(t
(cnblogs-other-mode-buffer-to-post))))
(defun cnblogs-check-file-in-entry-list (src-file)
"检查文件是否已经在列表项中"
(let ((res nil))
(mapc (lambda (entry)
(or res
(setq res
(equal src-file (nth 4 entry)))))
cnblogs-entry-list)
res))
(defun cnblogs-delete-post-from-entry-list (postid)
"通过postid将相应的entry的postid设置为nil并删除posts目录下相应的文件,成功返回t.POSTID是string类型或者int类型"
(if (integerp postid)
(setq postid (int-to-string postid)))
(condition-case ()
(progn
(setq cnblogs-entry-list
(mapcar (lambda (entry)
(if (equal postid
(if (integerp (nth 2 entry))
(int-to-string (nth 2 entry))
(nth 2 entry)))
(progn
(setcar (nthcdr 2 entry) nil)
(setcar (nthcdr 3 entry) nil)
(setcar (nthcdr 5 entry) "UNPUBLISHED")))
entry)
cnblogs-entry-list))
(cnblogs-save-entry-list)
(and (file-exists-p (concat cnblogs-file-post-path postid))
(delete-file (concat cnblogs-file-post-path postid)))
t)
(error nil)))
(defun cnblogs-import-directory (directory)
;; 滤掉所有以.开头的文件,这样就没有了..和.以及所有的隐藏文件
;; 滤掉所有以~结尾的文件,这样就没有了自动备份
(let ((files (directory-files directory t "^[^.].*[^~]$" t)))
(mapc (lambda (file)
;目录
(cond ((file-directory-p file)
(cnblogs-import-directory file))
;合法文件
((member (file-name-extension file) cnblogs-src-file-extension-list)
(cnblogs-push-src-file-to-entry-list file))))
files)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;功能函数;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun cnblogs-import-current-file ()
"将当前文件加入到库中(增加到博文项cnblogs-entry-list中)"
(interactive)
(let ((src-file (buffer-file-name)))
(if (member (file-name-extension src-file)
cnblogs-src-file-extension-list)
(progn
(cnblogs-push-src-file-to-entry-list src-file)
(cnblogs-save-entry-list)
(message "Succeed!"))
(message "Failed: UNSUPPORTED file!"))))
(defun cnblogs-import-file ()
"添加一个文件加入到库中(增加到博文项cnblogs-entry-list中)"
(interactive)
(let ((src-file (read-file-name "Import file: ")))
(if (member (file-name-extension src-file)
cnblogs-src-file-extension-list)
(progn
(cnblogs-push-src-file-to-entry-list src-file)
(cnblogs-save-entry-list)
(message "Succeed!"))
(message "Failed: UNSUPPORTED file!"))))
(defun cnblogs-import-folder ()
"递归添加一个目录中的所有合法文件到库中,这个是给用户用的,主要是调用cnblogs-import-directory"
(interactive)
(let ((directory (read-directory-name "Import folder: ")))
(cnblogs-import-directory directory)
(cnblogs-save-entry-list)))
(defun cnblogs-setup-blog ()
(interactive)
(unless (stringp cnblogs-blog-id)
(setq cnblogs-blog-id
(read-string "Your blog ID:" nil nil)))
(unless (stringp cnblogs-user-name)
(setq cnblogs-user-name
(read-string "Your username:" nil nil)))
(unless (stringp cnblogs-user-passwd)
(setq cnblogs-user-passwd
(read-passwd "Your password:" nil )))
(unless (stringp cnblogs-server-url)
(setq cnblogs-server-url
(concat blog-base-url
cnblogs-blog-id
"/services/metaweblog.aspx")))
(unless (stringp cnblogs-category-list)
(setq cnblogs-category-list
(cnblogs-metaweblog-get-categories)))
(unless (stringp cnblogs-blog-info)
(setq cnblogs-blog-info
(cnblogs-metaweblog-get-users-blogs)))
(or (file-directory-p cnblogs-file-root-path)
(make-directory cnblogs-file-root-path))
;; 如果没有posts目录,则新建这个目录
(or (file-directory-p cnblogs-file-post-path)
(make-directory cnblogs-file-post-path))
;; (if cnblogs-blog-info
;; (progn
;; (customize-save-variable 'cnblogs-blog-id cnblogs-blog-id)
;; (customize-save-variable 'cnblogs-user-name cnblogs-user-name)
;; (customize-save-variable 'cnblogs-user-passwd cnblogs-user-passwd)
;; (customize-save-variable 'cnblogs-server-url cnblogs-server-url)
;; ;; 如果没有根目录,则新建这个目录
;; (cnblogs-save-category-list)
;; ;; 从博客下载博文
;; (and (yes-or-no-p "Should I pull all your posts now, it may talk a long time?")
;; (let ((posts (cnblogs-metaweblog-get-recent-posts 0)))
;; (mapc (lambda (post)
;; (cnblogs-push-post-to-entry-list post))
;; posts))
;; (cnblogs-save-entry-list))
;; (message "设置成功"))
;; (message "设置失败"))
)
(defun cnblogs-new-post ()
(interactive)
(cnblogs-setup-blog)
(if (cnblogs-check-legal-for-publish (buffer-file-name))
;; 下面发布处理
(let* ((postid ;得到博文id
(cnblogs-metaweblog-new-post (cnblogs-current-buffer-to-post) t))
;得到博文内容
(post (cnblogs-metaweblog-get-post postid)))
;todo:这里要刷新列表
;;保存博文项和博文内容
(if (integerp postid)
(setq postid (int-to-string postid)))
;;保存博文
(with-temp-file (concat cnblogs-file-post-path postid)
(print post (current-buffer)))
(if (cnblogs-check-file-in-entry-list (buffer-file-name))
(cnblogs-assign-post-to-file post (buffer-file-name))
(push
;id
(list (cnblogs-gen-id)
;title
(cdr (assoc "title" post))
;postid
postid
;categories
(cdr (assoc "categories" post))
(buffer-file-name)
"PUBLISHED")
cnblogs-entry-list))
;保存博文项列表
(cnblogs-save-entry-list)
(message "Post published!"))))
(defun cnblogs-save-draft ()
(interactive)
(let ((postid
(cnblogs-metaweblog-new-post (cnblogs-current-buffer-to-post)
nil)))
(setq cnblogs-entry-list
(cons
(cnblogs-metaweblog-get-post postid)
cnblogs-entry-list))
(cnblogs-save-entry-list))
(message "保存草稿成功!"))
(defun cnblogs-delete-post ()
(interactive)
(cnblogs-setup-blog)
(if (cnblogs-check-legal-for-delete (buffer-file-name))
(let ((postid
(cnblogs-get-postid-by-src-file-name (buffer-file-name))))
(if (and postid
(yes-or-no-p "Are you sure?")
(cnblogs-metaweblog-delete-post postid t)
(cnblogs-delete-post-from-entry-list postid)
(cnblogs-save-entry-list))
(message "Succeed!")
(message "Failed!")))))
(defun cnblogs-edit-post ()
;;todo:更新本地
(interactive)
(if (cnblogs-check-legal-for-edit (buffer-file-name))
(let ((postid
(cnblogs-get-postid-by-src-file-name
(buffer-file-name))))
(if (and postid
(yes-or-no-p "Are you sure to update?")
(cnblogs-metaweblog-edit-post postid
(cnblogs-current-buffer-to-post)
t)
(cnblogs-assign-post-to-file (cnblogs-metaweblog-get-post postid)
(buffer-file-name))
(cnblogs-save-entry-list))
(message "Succeed!")
(message "Failed!")))))
(defun cnblogs-get-post ()
(interactive)
(let* ((postid
(read-string "Post ID:"))
(post
(condition-case ()
(cnblogs-metaweblog-get-post postid)
(error nil))))
(if (and postid
(cnblogs-delete-post-from-entry-list postid)
post
(setq cnblogs-entry-list
(cons post cnblogs-entry-list)))
(message "获取成功!")
(message "获取失败"))))
;; 获取并保存分类
(defun cnblogs-get-categories ()
(interactive)
(setq cnblogs-category-list
(condition-case ()
(cnblogs-metaweblog-get-categories)
(error nil)))
(if cnblogs-category-list
(progn
(cnblogs-save-category-list)
(message "获取分类成功!"))
(message "获取分类失败")))
(defun cnblogs-get-recent-posts ()
(interactive)
(let* ((num (read-number "输入要获取的随笔篇数:"
1))
(posts (condition-case ()
(cnblogs-metaweblog-get-recent-posts num)
(error nil))))
(if (not posts)
(message "获取失败!")
(progn
(mapc (lambda (post)
(cnblogs-push-post-to-entry-list post))
posts)
(cnblogs-save-entry-list)
(message "获取成功!")))))
(defun cnblogs-get-users-blogs ()
(interactive)
(setq cnblogs-blog-info
(condition-case ()
(prog1
(cnblogs-metaweblog-get-users-blogs)
(message "获取用户博客信息成功!"))
(error cnblogs-blog-info))))
(defun cnblogs-add-props (str plist)
"将faces属性plist赋给str,并返回这个str"
(set-text-properties 0 (length str) plist str)
str)
;;[c][b]
;;(defun cnblogs-category-selection-toggle (c)
;; "根据字符c查找要触发的分类,然后触发这个分类"
;; (let* ((begin (string-match (concat "[" c "]" [ ]*)
;; (buffer-substring (string-match "随笔分类" (buffer-string)) (point-max)))
;;
;; (substring (buffer-substring (point-min) (point-max)) 23728 23731 )
;; ))))
;;
;;(defun cnblogs-category-selection ()
;; (interactive)
;; (save-window-excursion
;; (delete-other-windows)
;; (split-window-vertically)
;; (org-switch-to-buffer-other-window (get-buffer-create " *Cnblogs categories*"))
;; (erase-buffer)
;; ;; 列出当前已经选择的分类
;; (insert "Current: ")
;;
;; ;; 列出随笔分类
;; (insert " 随笔分类: ")
;; (let* ((idx ?0)
;; (ctgr-list (remove-if-not (lambda (ctgr)
;; (equal (substring ctgr 1 5) "随笔分类"))
;; cnblogs-category-list))
;; (maxlen (apply 'max (mapcar 'length ctgr-list))))
;;
;; (mapc (lambda (ctgr)
;; (insert "[" idx "]" (format "%s " (substring ctgr 6)))
;; (setq idx (1+ idx)))
;; ctgr-list))
;;
;; ;; 列出网站类分
;; (insert " 网站分类: ")
;; (let* ((idx ?A)
;; (ctgr-list (remove-if-not (lambda (ctgr)
;; (equal (substring ctgr 1 5) "网站分类"))
;; cnblogs-category-list))
;; (maxlen (apply 'max (mapcar 'length ctgr-list))))
;; (mapc (lambda (ctgr)
;; (insert "[" idx "]" (format "%s " (substring ctgr 6)))
;; (setq idx (1+ idx)
;; ))
;; ctgr-list))
;; (insert " 其他分类: ")
;; ;; 列出其他分类
;; (mapc (lambda (ctgr)
;; (if (equal (substring ctgr 1 3) "发布")
;; (insert (format "%s " ctgr)))
;; )
;; cnblogs-category-list)
;; (message "[0..9..a-z..]:Toggle [SPC]:clear [RET]:accept")
;; ;; 处理分类选择
;; (catch 'exit
;; (while t
;; (let ((c (read-char-exclusive)))
;; (cond
;; ((= c ? ) (throw exit t))
;; (t (do nothing)
;; )
;; ))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mode设置;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 下面是关于minor mode的内容
(defun cnblogs-init ()
"Cnblogs的所有初始化工作,加载各种变量值."
;;加载博文项列表
(cnblogs-load-entry-list)
;;加载博文分类
(cnblogs-load-category-list)
;;将博文项列表中的项加入到相应的分类中去
(mapc (lambda (categorie)
(progn
;;先将该分类加入
(push (cons categorie nil)
cnblogs-posts-in-category)
)
;;将属于该分类的项加入该分类
(mapc (lambda (entry)
(let* ((entry-categories (nth 3 entry))
(flag (member categorie entry-categories)))
(and flag
(push entry
(cdr (assoc categorie cnblogs-posts-in-category)))))
)
cnblogs-entry-list))
cnblogs-category-list)
)
;; 定义菜单
(define-key cnblogs-mode-map [menu-bar menu-bar-cnblogs-menu]
(cons "Cnblogs" cnblogs-mode-map))
(define-minor-mode cnblogs-minor-mode
;; "cnblogs-minor-mode"
:init-value nil
:lighter " Cnblogs"
:keymap cnblogs-mode-map
:group Cnblogs)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KeyMap;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-key cnblogs-mode-map (kbd "C-c c p") 'cnblogs-new-post)
(define-key cnblogs-mode-map (kbd "C-c c s") 'cnblogs-save-draft)
(define-key cnblogs-mode-map (kbd "C-c c d") 'cnblogs-delete-post)
(define-key cnblogs-mode-map (kbd "C-c c e") 'cnblogs-edit-post)
(define-key cnblogs-mode-map (kbd "C-c c g") 'cnblogs-get-post)
(define-key cnblogs-mode-map (kbd "C-c c c") 'cnblogs-get-categories)
(define-key cnblogs-mode-map (kbd "C-c c r") 'cnblogs-get-recent-posts)
(define-key cnblogs-mode-map (kbd "C-c c u") 'cnblogs-get-users-blogs)
;;(add-hook 'cnblogs-minor-mode-hook 'cnblogs-init) ;打开cnblogs-minor-mode时再加载数据等初始化
(cnblogs-init)
(cnblogs-minor-mode)
(setq org-export-show-temporary-export-buffer nil)
(provide 'cnblogs)
;;; cnblogs.el ends here.
经过修改后,这个世界安静了。
3 配置方法及修改内容说明
- 下载文件 打开github:huwenbiao, 下载cnblogs.el和metaweblog.el 两个文件,至于另外一个文件xml-rpc.el 请到 GITHUB:hexmode 下载。
- 如何加载 这些是针对小白的。大牛忽略。
将这三个文件放到你的.emacs.d/cnblogs 中. 然后在init.el中 添加如下代码:
;; for cnblogs. (add-to-list 'load-path "~/.emacs.d/cnblogs/") (require 'cnblogs)
这样,以后打开org文件时就会自动加载相关的配置。其中的cnblogs-minor-mode 就是用于发布博客的一个mode.
-
配置个人信息
打开cnblogs.el 文件,找到 通过defcustom定义的以下三个变量:
cnblogs-blog-id –> cnblogs 的blog id 比如网址:https://www.cnblogs.com/halberd-lee 的blog id 就是 halberd-lee
cnblogs-user-name –> 登录cnblogs的用户名
cnblogs-user-passwd –> 登录 cnblogs的密码 三个变量后面的值都是Nil , 修改为自己的信息。
4. 使用说明
做好以上配置后, 编辑好org-mode buffer后,不需要保存,C-c c p 即可正常发布,如果要本地保留源文件,最好还是保存。
C-c c d 根据发布记录的(post-id)删除博客中的博文。这是两个最常用的功能。