Permalink
Browse files

Merge remote-tracking branch 'upstream/master'

Conflicts:
	org2blog.el
  • Loading branch information...
2 parents 73206dd + 18e2e04 commit f1c9029b3db83e3c1a32e1827051045edfb649ed @sachac committed Sep 18, 2012
Showing with 1,613 additions and 459 deletions.
  1. +290 −65 README.org
  2. +197 −68 metaweblog.el
  3. +58 −0 org2blog-autoloads.el
  4. +4 −0 org2blog-pkg.el
  5. +927 −326 org2blog.el
  6. +137 −0 test-metaweblog.el
View
355 README.org
@@ -1,85 +1,310 @@
+#+TITLE: Org2blog Readme
+#+Options: num:nil
+#+STARTUP: odd
+#+Style: <style> h1,h2,h3 {font-family: arial, helvetica, sans-serif} </style>
+
* Overview
- org2blog provides a simple way of publishing from org-mode to
- [[http://wordpress.org/][WordPress]] blogs. Presently supports posting with categories, tags
- and images. It uploads images on the local system automatically to
- your wordpress blog.
+ org2blog/wp is a tool to publish directly from Emacs' org-mode to
+ [[http://wordpress.org/][WordPress]] blogs. org2blog/wp is one of the two packages named
+ org2blog. The other is [[http://repo.or.cz/r/org2blog.git/][org2blog/atom]] by [[http://tehom-blog.blogspot.com/][Tom Breton]].
+
+ org2blog/wp should, /ideally/, work with any platform that supports
+ xml-rpc but some of wordpress specific features may not work.
- It is inspired by and based on a [[http://paste.lisp.org/display/69993][wordpress posting client for
+ org2blog was inspired by and based on a [[http://paste.lisp.org/display/69993][wordpress posting client for
muse-mode]] written by [[http://www.emacswiki.org/emacs/AshishShukla][Ashish Shukla]].
org2blog is licensed under GPLv3
* Installation
- Download org2blog for github
+ Download org2blog from GitHub or Marmalade
: git clone http://github.com/punchagan/org2blog.git
-
+
+ : http://marmalade-repo.org/packages/org2blog
+
Add the org2blog directory to your load path and then add
- : (require 'org2blog)
+ : (setq load-path (cons "~/.emacs.d/org2blog/" load-path))
+ : (require 'org2blog-autoloads)
- to your dot emacs. You can optionally save the xmlrpc url of your
- blog and the username.
+ to your dot emacs.
- : (setq org2blog-server-url "http://username.wordpress.com/xmlrpc.php"
- : org2blog-server-user "username"
- : org2blog-server-weblog-id "")
+*** Dependencies
+ 1. org2blog depends on /xml-rpc/ available at [[http://launchpad.net/xml-rpc-el][Launchpad]]. Add it
+ to your /load-path/ before using org2blog.
- If you use only categories, set the org2blog-use-tags-as-categories
- variable.
+ 2. Make sure you have the latest org-mode installed. It may work
+ with versions > 7, but it is best to have the latest org-mode
+ if you can.
- : (setq org2blog-use-tags-as-categories t)
- *Note* - org2blog depends on xml-rpc available at [[http://launchpad.net/xml-rpc-el][Launchpad]]. Add it
- to your load-path before using org2blog.
-
* Usage
- 1. Log in using *M-x org2blog-login*. Provide xmlrpc url, username,
- password and blog-id (not in use now; useful when editing posts).
- Optionally, you could set these in your .emacs.
-
- 2. To start writing a new post use *M-x org2blog-new-entry*. Tags
- to your post are comma separated values passed to KEYWORDS.
- Categories are comma separated values passed to DESCRIPTION.
- Completion (for both) is triggered using *C-c t*. If a new
- category is used you will be prompted before adding it.
-
- 3. Use *C-c p* to Publish or *C-c d* to post as draft. Use *C-c P*
- to Publish as page or *C-c D* to save page as draft.
-
- 4. You could also publish your exiting org files.
- - Add #+TITLE, #+OPTIONS, #+KEYWORDS, #+DESCRIPTION, #+DATE,
- to your file. All of these are Optional. If a #+TITLE is
- absent, your post will be untitled.
- - If you wish to edit an existing post, add a #+POSTID.
- - To post as draft - *M-x org2blog-post-entry*
- - To publish - *C-u M-x org2blog-post-entry*
- - Alternatively, you could enable org2blog-mode using *M-x
- org2blog-mode*. This provides the key-bindings *C-c p*, *C-c
- d* and *C-c t*.
- - To post entry as a page, use *M-x org2blog-post-entry-as-page*
-
- 5. Thanks to Sacha, you can now also post a subtree with *M-x
- org2blog-post-subtree*. Tags of the subtree will be used for
- tags. Other properties can be set using a property drawer.
-
- 6. To delete an entry or a page -
- - If you are in the buffer of the entry/page, with #+POST_ID
- present on the page, use *M-x org2blog-delete-entry* or *M-x
- org2blog-delete-page*
- - If you want to delete a particular post (whose post-id is
- known) from any buffer, use *C-u post-id M-x
- org2blog-delete-entry*. Similarly, for a page.
-
-* Miscellaneous
+*** Defining your blog setup
+
+ *Note* - This setup works only for org2blog version >=0.3. For
+ lower versions of org2blog check the README from the commit
+ e37126e
+
+ You can (should) tell org2blog where your blogs are and optionally
+ specify different values for some options. This is done by
+ customising the variable =org2blog/wp-blog-alist=.
+
+ For example:
+ : (setq org2blog/wp-blog-alist
+ : '(("wordpress"
+ : :url "http://username.wordpress.com/xmlrpc.php"
+ : :username "username"
+ : :default-title "Hello World"
+ : :default-categories ("org2blog" "emacs")
+ : :tags-as-categories nil)
+ : ("my-blog"
+ : :url "http://username.server.com/xmlrpc.php"
+ : :username "admin")))
+
+ This defines two blogs identified by the terms =wordpress= and
+ =my-blog=.
+
+ The variables in the =plist= set for each blog over-ride the
+ global values of the corresponding variables. =url= and =username=
+ are mandatory variables. Others are optional. If you customize the
+ variable =org2blog/wp-blog-alist= there is detail information about the
+ meaning of each property.
+
+ You can also use =.netrc= file to save your usernames and
+ passwords.
+
+ Your =.netrc= should look like this
+
+ : machine myblog login myusername password myrealpassword
+
+ Then, use these details, as shown below.
+
+ : (setq blog (netrc-machine (netrc-parse "~/.netrc") "myblog" t))
+ : (setq org2blog/wp-blog-alist
+ : '(("my-blog"
+ : :url "http://username.server.com/xmlrpc.php"
+ : :username (netrc-get blog "login")
+ : :password (netrc-get blog "password"))))
+
+
+ To browse or customize other variables use the customize group
+ function.
+
+ : M-x customize-group org2blog/wp RET
+
+*** Logging In
+ Use *M-x org2blog/wp-login*. This function ask you for the name of one
+ of the blogs you have configured in the org2blog/wp-blog-alist
+ variable described above and will ask for your password. The
+ counterpart of this function is *M-x org2blog/wp-logout*, which will
+ log you out of the active blog.
+
+*** Writing a new post
+ Use *M-x org2blog/wp-new-entry*.
+ - Tags and Categories are comma or space separated values.
+ Completion (for both) is triggered using *C-c t*. If a new
+ category is used you will be prompted before adding it.
+ - The excerpt for a post can be written as =#+DESCRIPTION=.
+ - The Permalink of a post can be set using =#+PERMALINK=.
+ - The parent for a page can be specified using =#+PARENT=. *C-c
+ t* again provides completion for the page name.
+
+*** Using post templates
+ Custom post templates can be specified by modifying variables
+ =org2blog/wp-buffer-template= and =org2blog/wp-buffer-format-function=.
+
+ For example:
+
+ : (setq org2blog/wp-buffer-template
+ : "-----------------------
+ : #+TITLE: %s
+ : #+DATE: %s
+ : -----------------------\n")
+ :
+ : (defun my-format-function (format-string)
+ : (format format-string
+ : org2blog/wp-default-title
+ : (format-time-string "%d-%m-%Y" (current-time)))))
+ : (setq org2blog/wp-buffer-format-function 'my-format-function)
+
+ =org2blog/wp-new-entry= will now create buffers begining with a default
+ title and the current date.
+
+*** Updating an existing post
+ To update a blog post, you can simply edit the content of the org
+ file and republish using ~org2blog~. The =POSTID= saved in the
+ file, ensures that the same blog post is updated, and a new post
+ is not made.
+
+*** Publishing
+ | post buffer as draft | *C-c d* | *M-x org2blog/wp-post-buffer* |
+ | publish buffer | *C-c p* | *C-u M-x org2blog/wp-post-buffer* |
+ | post buffer as page draft | *C-c D* | *M-x org2blog/wp-post-buffer-as-page* |
+ | publish buffer as page | *C-c P* | *C-u M-x org2blog/wp-post-buffer-as-page* |
+
+*** Post your existing org-files too
+ - Add =#+TITLE=, =#+OPTIONS=, =#+TAGS=, =#+CATEGORY=,
+ =#+DESCRIPTION=, =#+DATE=, to your file. (All of these are
+ Optional)
+ - Add a =#+POSTID= to edit an existing post.
+ - Post buffer using the function names, as above.
+*** Posting a subtree
+ - Use *M-x org2blog/wp-post-subtree*.
+ - Tags of the subtree will be used for tags.
+ - Other properties can be set using a property drawer. The
+ properties use the same names as the buffer variables.
+ - =POST_DATE= sets the date to be used for the post. If it is not
+ present, =SCHEDULED=, =DEADLINE= or any other active or inactive
+ time-stamps are used.
+ - The headline is used as the title of the post. But, =TITLE= can
+ be used to use a different title.
+*** Posting source code blocks
+ Babel source blocks or example code is automatically posted in
+ =<pre>= tags. You can ask =org2blog= to use Wordpress's
+ sourcecode shortcode blocks. To use this, you need to set the
+ variable =org2blog/wp-use-sourcecode-shortcode= and also add
+ =htmlize.el= (available in org-mode's =contrib/lisp=) to your
+ =load-path=. Wordpress's sourcecode shortcode blocks can be given
+ various [[http://en.support.wordpress.com/code/posting-source-code/#configuration-parameters][configuration parameters]]. These can be passed to the
+ exported sourcecode shortcode blocks, by passing them to the babel
+ blocks using =:syntaxhl= parameter. You could also modify the
+ default arguments passed to sourcecode shortcode blocks by
+ customizing the =org2blog/wp-sourcecode-default-params= variable.
+*** Delete an entry or a page
+ - If you are in the buffer of the entry/page, with =#+POSTID=
+ present on the page, use:
+
+ *M-x org2blog/wp-delete-entry* or *M-x org2blog/wp-delete-page*
+
+ - If you want to delete a particular post (whose post-id is known)
+ from any buffer, use
+
+ *C-u post-id M-x org2blog/wp-delete-entry*.
+
+ Similarly, for a page.
+
+*** Enabling org2blog mode (defines key-bindings)
+ - use *M-x org2blog/wp-mode*.
+ This defines the following the key-bindings:
+ | *C-c p* | publish buffer |
+ | *C-c d* | post as draft |
+ | *C-c t* | complete tag or parent page name |
+
+*** Uploading Images or Files
+ In-line images and linked images (or files) with file:// urls will
+ be uploaded to the media library and the links will be updated.
+ Information about uploaded files is added as a comment to the post
+ itself.
+
+ Captions and attributes as [[http://orgmode.org/manual/Images-in-HTML-export.html][defined]] in org-mode will be preserved,
+ but these attributes are not saved with the image to the library
+ itself.
+
+*** "Dashboard" of all posts
+ ~org2blog~ makes it easy to manage your blog-posts by actually
+ keeping track of all the posts you make from it, in a simple
+ org-table. By default it is saved in a file ~.org2blog.org~ in
+ the ~org-directory~. This is meant to be a dashboard of sorts,
+ and is an optional feature that can be turned off.
+* Miscellaneous
1. You may want to look at the [[http://orgmode.org/manual/Export-options.html#Export-options][Export options]] and [[http://orgmode.org/manual/HTML-export.html#HTML-export][HTML export]]
sections of the org-manual.
- 2. If you wish to post to blogger from org-mode, look at the
- [[http://code.google.com/p/emacspeak/source/browse/trunk/lisp/g-client/org2blogger.el][g-client extension]] by [[http://en.wikipedia.org/wiki/T._V._Raman][T. V. Raman]].
- 3. If you have an issue/bug/feature request, use the issue tracker
- on git or drop a mail to punchagan+org2blog[at]gmail[dot]com.
- 4. If you haven't found a bug/issue, but have tried out and/or are
- using org2blog to post to your blog, post me a line on how you
- use it and on what blog. I'd love to hear from you!
-
+
+ 2. If you wish to post to blogger from org-mode, look at -
+
+ 1. [[http://repo.or.cz/r/org2blog.git/][org2blog/atom]], a g-client extension by [[http://tehom-blog.blogspot.com/][Tom Breton]]
+
+ 2. [[http://github.com/rileyrg/org-googlecl][org-googlecl]] by Richard Riley -- uses [[http://code.google.com/p/googlecl/][googlecl]]
+
+ 3. Please go through the README and the FAQ, before writing to me.
+ Also, looking at =M-x customize-group org2blog-wp= might help.
+
+ 4. If you have an issue/bug/feature request, use the issue tracker
+ on git or drop a mail to punchagan+org2blog[at]gmail[dot]com.
+ I'd also appreciate patches/suggestions to improve the
+ documentation. Feel free to drop in with general comments, too.
+ I'd love to hear from you! NOTE: If you are using the package
+ from ELPA/Marmalade, please try using the latest =git= version
+ before filing a bug report.
+
+ 5. Feel free to add your site to the list of sites using org2blog,
+ on the [[https://github.com/punchagan/org2blog/wiki/Blogs-using-org2blog][wiki]] at github.
+
+* FAQ
+ - How many blogs can I configure with org2blog?
+
+ You can configure and use any number of blogs with org2blog/wp.
+ Use the ~org2blog/wp-blog-alist~ variable to configure each blog.
+ Look at the [[Defining your blog setup]] section, in the README. But,
+ note that you can be logged in, to just one blog at a time.
+
+ - How do I change the default title of a new post?
+
+ : (setq org2blog/wp-default-title "My New Title")
+
+ - How do I change the default title for one blog alone?
+
+ Set the relevant (~:default-title~) variable in the
+ ~org2blog/wp-blog-alist~ variable.
+
+ - I do not use tags. I wish to use them as categories. How?
+
+ Setting the ~org2blog/wp-use-tags-as-categories~ variable will do
+ that for all the blogs.
+
+ : (setq org2blog/wp-use-tags-as-categories t)
+
+ Instead, you can set the corresponding variable for each blog that
+ you are using.
+
+ - Can I configure org2blog to confirm before publishing a post?
+
+ : (setq org2blog/wp-confirm-post t)
+
+ - I wish to customize the default template of a new post. How do I
+ do it?
+
+ Customize the variable =org2blog/wp-buffer-template=.
+
+ - New-lines are not handled properly. Why?
+
+ The behaviour of new-lines has been working since a little before
+ version 0.3. It is highly recommended that you use the latest git
+ version of org2blog.
+
+ By default, new lines are stripped off from the org buffer. To
+ retain new-lines, unset the =org2blog/wp-keep-new-lines=
+ variable.
+
+ - Why aren't my SRC blocks not enclosed in =[sourcecode]
+ [/sourcecode]= block?
+
+ Set the =org2blog/wp-use-sourcecode-shortcode= variable to turn on
+ this behaviour. You may also set it at a per-blog level, if you
+ choose.
+
+ - Is wordpress the only CMS/Blog-engine that org2blog/wp supports?
+
+ Any blog engine using the metaweblog API should work,
+ theoretically. But, it is not tested with other blog engines.
+
+ One happy user reports that org2blog [[https://github.com/punchagan/org2blog/issues/issue/37][also works]] with the [[http://www.doclear.net/][Dotclear]]
+ weblog engine.
+
+ - Is there a way to import from wordpress or other blogs into the
+ org2blog post format?
+
+ I've a simple [[https://github.com/punchagan/org2blog-importers/blob/master/wp_to_org2blog.py][python script]] that uses ~pandoc~ to convert from
+ Wordpress export xml to org2blog posts. It could easily be
+ tweaked to write importers for other kinds of blogs.
+
+ - How do I split a post into an introductory paragraph and a full
+ view.
+
+ Just put in
+
+ : #+HTML: <!--more-->
+
+ at the location where you wish to split the post.
View
265 metaweblog.el
@@ -1,4 +1,4 @@
-;; metaweblog.el -- an emacs library to access metaweblog based weblogs
+;;; metaweblog.el --- an emacs library to access metaweblog based weblogs
;; Copyright (C) 2008 Ashish Shukla
;; Copyright (C) 2010 Puneeth Chaganti
@@ -42,25 +42,44 @@
user-name
password))
-(defun metaweblog-new-post
+(defun wp-get-pages (blog-xmlrpc user-name password blog-id)
+ "Retrieves list of pages from the weblog system. Uses wp.getPages."
+ (xml-rpc-method-call blog-xmlrpc
+ "wp.getPages"
+ blog-id
+ user-name
+ password))
+
+(defun wp-get-pagelist (blog-xmlrpc user-name password blog-id)
+ "Retrieves list of pages (minimal information) from the weblog
+system. Uses wp.getPageList."
+ (xml-rpc-method-call blog-xmlrpc
+ "wp.getPageList"
+ blog-id
+ user-name
+ password))
+
+(defun metaweblog-new-post
(blog-xmlrpc user-name password blog-id content publish)
"Sends a new post to the blog. If PUBLISH is non-nil, the post is
published, otherwise it is saved as draft. CONTENT will be an alist
title, description, categories, and date as keys (string-ified) mapped to the
-title of the post, post contents, list of categories, and date respectively."
+title of the post, post contents, list of categories, and date respectively."
(let ((post-title (cdr (assoc "title" content)))
(post-description (cdr (assoc "description" content)))
(post-categories (cdr (assoc "categories" content)))
(post-tags (cdr (assoc "tags" content)))
+ (post-excerpt (cdr (assoc "excerpt" content)))
+ (post-permalink (cdr (assoc "permalink" content)))
(post-date (cdr (assoc "date" content))))
;;; since xml-rpc-method-call entitifies the HTML text in the post
;;; we've to use raw
(xml-rpc-xml-to-response (xml-rpc-request
blog-xmlrpc
`((methodCall
nil
- (methodName nil "metaWeblog.newPost")
- (params nil
+ (methodName nil "metaWeblog.newPost")
+ (params nil
(param nil (value nil (string nil ,blog-id)))
(param nil (value nil (string nil ,user-name)))
(param nil (value nil (string nil ,password)))
@@ -74,27 +93,33 @@ title of the post, post contents, list of categories, and date respectively."
(name nil "description")
(value nil ,post-description))
(member nil
+ (name nil "mt_excerpt")
+ (value nil ,post-excerpt))
+ (member nil
+ (name nil "wp_slug")
+ (value nil ,post-permalink))
+ (member nil
(name nil "dateCreated")
(dateTime.iso8601 nil ,post-date))
,(when post-tags
- `(member nil
+ `(member nil
(name nil "mt_keywords")
(value nil
(array
nil
- ,(append
+ ,(append
'(data nil)
(mapcar
(lambda(f)
`(value nil (string nil ,f)))
post-tags))))))
,(when post-categories
- `(member nil
+ `(member nil
(name nil "categories")
(value nil
(array
nil
- ,(append
+ ,(append
'(data nil)
(mapcar
(lambda(f)
@@ -107,21 +132,23 @@ title of the post, post contents, list of categories, and date respectively."
"Sends a new page to the blog. If PUBLISH is non-nil, the post is
published, otherwise it is saved as draft. CONTENT will be an alist
title, description, categories, and date as keys (string-ified) mapped to the
-title of the post, post contents, list of categories, and date respectively."
+title of the post, post contents, list of categories, and date respectively."
(let ((post-title (cdr (assoc "title" content)))
(post-description (cdr (assoc "description" content)))
(post-categories (cdr (assoc "categories" content)))
(post-tags (cdr (assoc "tags" content)))
+ (post-excerpt (cdr (assoc "excerpt" content)))
+ (post-permalink (cdr (assoc "permalink" content)))
+ (post-parent (cdr (assoc "parent" content)))
(post-date (cdr (assoc "date" content))))
- (message post-date)
;;; since xml-rpc-method-call entitifies the HTML text in the post
;;; we've to use raw
(xml-rpc-xml-to-response (xml-rpc-request
blog-xmlrpc
`((methodCall
nil
- (methodName nil "wp.newPage")
- (params nil
+ (methodName nil "wp.newPage")
+ (params nil
(param nil (value nil (string nil ,blog-id)))
(param nil (value nil (string nil ,user-name)))
(param nil (value nil (string nil ,password)))
@@ -135,44 +162,131 @@ title of the post, post contents, list of categories, and date respectively."
(name nil "description")
(value nil ,post-description))
(member nil
+ (name nil "mt_excerpt")
+ (value nil ,post-excerpt))
+ (member nil
+ (name nil "wp_slug")
+ (value nil ,post-permalink))
+ (member nil
+ (name nil "wp_page_parent_id")
+ (value nil ,post-parent))
+ (member nil
(name nil "dateCreated")
(dateTime.iso8601 nil ,post-date))
,(when post-tags
- `(member nil
+ `(member nil
(name nil "mt_keywords")
(value nil
(array
nil
- ,(append
+ ,(append
'(data nil)
(mapcar
(lambda(f)
`(value nil (string nil ,f)))
post-tags))))))
,(when post-categories
- `(member nil
+ `(member nil
(name nil "categories")
(value nil
(array
nil
- ,(append
+ ,(append
'(data nil)
(mapcar
(lambda(f)
`(value nil (string nil ,f)))
post-categories)))))))))
(param nil (value nil (boolean nil ,(if publish "1" "0")))))))))))
-(defun metaweblog-edit-post
+(defun wp-edit-page
+ (blog-xmlrpc user-name password blog-id post-id content publish)
+ "Edits an existing page on the blog. If PUBLISH is non-nil, the
+post is published, otherwise it is saved as draft. CONTENT will
+be an alist title, description, categories, and date as
+keys (string-ified) mapped to the title of the post, post
+contents, list of categories, and date respectively."
+ (let ((post-title (cdr (assoc "title" content)))
+ (post-description (cdr (assoc "description" content)))
+ (post-categories (cdr (assoc "categories" content)))
+ (post-tags (cdr (assoc "tags" content)))
+ (post-excerpt (cdr (assoc "excerpt" content)))
+ (post-permalink (cdr (assoc "permalink" content)))
+ (post-parent (cdr (assoc "parent" content)))
+ (post-date (cdr (assoc "date" content))))
+ (message post-date)
+ ;;; since xml-rpc-method-call entitifies the HTML text in the post
+ ;;; we've to use raw
+ (xml-rpc-xml-to-response
+ (xml-rpc-request
+ blog-xmlrpc
+ `((methodCall
+ nil
+ (methodName nil "wp.editPage")
+ (params nil
+ (param nil (value nil (string nil ,blog-id)))
+ (param nil (value nil (string nil ,post-id)))
+ (param nil (value nil (string nil ,user-name)))
+ (param nil (value nil (string nil ,password)))
+ (param nil (value nil
+ (struct
+ nil
+ (member nil
+ (name nil "title")
+ (value nil ,post-title))
+ (member nil
+ (name nil "description")
+ (value nil ,post-description))
+ (member nil
+ (name nil "mt_excerpt")
+ (value nil ,post-excerpt))
+ (member nil
+ (name nil "wp_slug")
+ (value nil ,post-permalink))
+ (member nil
+ (name nil "wp_page_parent_id")
+ (value nil ,post-parent))
+ (member nil
+ (name nil "dateCreated")
+ (dateTime.iso8601 nil ,post-date))
+ ,(when post-tags
+ `(member nil
+ (name nil "mt_keywords")
+ (value nil
+ (array
+ nil
+ ,(append
+ '(data nil)
+ (mapcar
+ (lambda(f)
+ `(value nil (string nil ,f)))
+ post-tags))))))
+ ,(when post-categories
+ `(member nil
+ (name nil "categories")
+ (value nil
+ (array
+ nil
+ ,(append
+ '(data nil)
+ (mapcar
+ (lambda(f)
+ `(value nil (string nil ,f)))
+ post-categories)))))))))
+ (param nil (value nil (boolean nil ,(if publish "1" "0")))))))))))
+
+(defun metaweblog-edit-post
(blog-xmlrpc user-name password post-id content publish)
- "Edits an exiting post, if post-id is given. If PUBLISH is non-nil, the
-post is published, otherwise it is saved as draft. CONTENT will be an alist
-title, description, categories, and date as keys (string-ified) mapped to the
+ "Edits an exiting post, if post-id is given. If PUBLISH is non-nil, the
+post is published, otherwise it is saved as draft. CONTENT will be an alist
+title, description, categories, and date as keys (string-ified) mapped to the
title of the post, post contents, list of categories, and date respectively."
(let ((post-title (cdr (assoc "title" content)))
(post-description (cdr (assoc "description" content)))
(post-categories (cdr (assoc "categories" content)))
(post-tags (cdr (assoc "tags" content)))
+ (post-excerpt (cdr (assoc "excerpt" content)))
+ (post-permalink (cdr (assoc "permalink" content)))
(post-date (cdr (assoc "date" content))))
(message post-date)
;;; since xml-rpc-method-call entitifies the HTML text in the post
@@ -181,8 +295,8 @@ title of the post, post contents, list of categories, and date respectively."
blog-xmlrpc
`((methodCall
nil
- (methodName nil "metaWeblog.editPost")
- (params nil
+ (methodName nil "metaWeblog.editPost")
+ (params nil
(param nil (value nil (string nil ,post-id)))
(param nil (value nil (string nil ,user-name)))
(param nil (value nil (string nil ,password)))
@@ -196,37 +310,43 @@ title of the post, post contents, list of categories, and date respectively."
(name nil "description")
(value nil ,post-description))
(member nil
+ (name nil "mt_excerpt")
+ (value nil ,post-excerpt))
+ (member nil
+ (name nil "wp_slug")
+ (value nil ,post-permalink))
+ (member nil
(name nil "dateCreated")
(dateTime.iso8601 nil ,post-date))
,(when post-tags
- `(member nil
+ `(member nil
(name nil "mt_keywords")
(value nil
(array
nil
- ,(append
+ ,(append
'(data nil)
(mapcar
(lambda(f)
`(value nil (string nil ,f)))
post-tags))))))
,(when post-categories
- `(member nil
+ `(member nil
(name nil "categories")
(value nil
(array
nil
- ,(append
+ ,(append
'(data nil)
(mapcar
(lambda(f)
`(value nil (string nil ,f)))
post-categories)))))))))
(param nil (value nil (boolean nil ,(if publish "1" "0")))))))))))
-(defun metaweblog-get-post(blog-xmlrpc user-name password post-id)
+(defun metaweblog-get-post (blog-xmlrpc user-name password post-id)
"Retrieves a post from the weblog. POST-ID is the id of the post
-which is to be returned"
+which is to be returned. Can be used with pages as well."
(xml-rpc-method-call blog-xmlrpc
"metaWeblog.getPost"
post-id
@@ -262,50 +382,59 @@ no. of posts that should be returned."
password
number-of-posts))
-(defun get-image-properties (file)
-"Gets the properties of an image file."
- (let* (image-base64 type name)
+(defun get-file-properties (file)
+ "Gets the properties of a file. Returns an assoc list with
+name - file name
+bits - data of the file as a base64 encoded string
+type - mimetype of file deduced from extension."
+ (let* (base64-str type name)
(save-excursion
(save-restriction
- (with-temp-buffer
- (set-buffer (find-file file))
+ (with-current-buffer (find-file-noselect file)
+ (fundamental-mode)
(setq name (file-name-nondirectory file))
- (setq image-base64 (base64-encode-string (buffer-string)))
- (setq type (symbol-name (image-type file)))
- (kill-buffer)
- (setq fff-image `(("name" . ,name)
- ("bits" . ,image-base64)
- ("type" . ,(concat "image/" type)))))))
- fff-image))
+ (setq base64-str (base64-encode-string (buffer-string)))
+ (setq type (mailcap-extension-to-mime (file-name-extension file)))
+ (kill-buffer)
+ (setq file-props `(("name" . ,name)
+ ("bits" . ,base64-str)
+ ("type" . ,type))))))
+ file-props))
-(defun metaweblog-upload-image (blog-xmlrpc user-name password blog-id image)
- "Uploads an image to the blog. IMAGE will be an alist name, type, bits, as keys mapped to name of the image, mime type of the image, image data in base 64, respectively."
- (let ((image-name (cdr (assoc "name" image)))
- (image-type (cdr (assoc "type" image)))
- (image-bits (cdr (assoc "bits" image))))
+(defun metaweblog-upload-file (blog-xmlrpc user-name password blog-id file)
+ "Uploads file to the blog. FILE will be an alist name, type,
+bits, as keys mapped to name of the file, mime type and the
+data."
+ (let ((file-name (cdr (assoc "name" file)))
+ (file-type (cdr (assoc "type" file)))
+ (file-bits (cdr (assoc "bits" file))))
- (xml-rpc-xml-to-response (xml-rpc-request
- blog-xmlrpc
- `((methodCall
- nil
- (methodName nil "metaWeblog.newMediaObject")
- (params nil
- (param nil (value nil (string nil ,blog-id)))
- (param nil (value nil (string nil ,user-name)))
- (param nil (value nil (string nil ,password)))
- (param nil (value nil
- (struct
- nil
- (member nil
- (name nil "name")
- (value nil ,image-name))
- (member nil
- (name nil "bits")
- (base64 nil ,image-bits))
- (member nil
- (name nil "type")
- (value nil ,image-type)))))
- )))))))
+ (xml-rpc-xml-to-response
+ (xml-rpc-request
+ blog-xmlrpc
+ `((methodCall
+ nil
+ (methodName nil "metaWeblog.newMediaObject")
+ (params nil
+ (param nil (value nil (string nil ,blog-id)))
+ (param nil (value nil (string nil ,user-name)))
+ (param nil (value nil (string nil ,password)))
+ (param nil (value nil
+ (struct
+ nil
+ (member nil
+ (name nil "name")
+ (value nil ,file-name))
+ (member nil
+ (name nil "bits")
+ (base64 nil ,file-bits))
+ (member nil
+ (name nil "type")
+ (value nil ,file-type))
+ (member nil
+ (name nil "overwrite")
+ (value nil "t")))))
+ )))))))
(provide 'metaweblog)
View
58 org2blog-autoloads.el
@@ -0,0 +1,58 @@
+
+;;;### (autoloads (org2blog/wp-preview-subtree-post org2blog/wp-preview-buffer-post
+;;;;;; org2blog/wp-track-subtree org2blog/wp-track-buffer org2blog/wp-post-subtree
+;;;;;; org2blog/wp-new-entry org2blog/wp-login org2blog/wp-mode)
+;;;;;; "org2blog" "org2blog.el" (19865 57364))
+;;; Generated autoloads from org2blog.el
+
+(autoload 'org2blog/wp-mode "org2blog" "\
+Toggle org2blog/wp mode.
+With no argument, the mode is toggled on/off.
+Non-nil argument turns mode on.
+Nil argument turns mode off.
+
+Commands:
+\\{org2blog/wp-entry-mode-map}
+
+Entry to this mode calls the value of `org2blog/wp-mode-hook'.
+
+\(fn &optional ARG)" t nil)
+
+(autoload 'org2blog/wp-login "org2blog" "\
+Logs into the blog. Initializes the internal data structures.
+
+\(fn)" t nil)
+
+(autoload 'org2blog/wp-new-entry "org2blog" "\
+Creates a new blog entry.
+
+\(fn)" t nil)
+
+(autoload 'org2blog/wp-post-subtree "org2blog" "\
+Post the current entry as a draft. Publish if PUBLISH is non-nil.
+
+\(fn &optional PUBLISH)" t nil)
+
+(autoload 'org2blog/wp-track-buffer "org2blog" "\
+Save details of current buffer in the tracking file.
+
+\(fn)" t nil)
+
+(autoload 'org2blog/wp-track-subtree "org2blog" "\
+Save details of current subtree in the tracking file.
+
+\(fn)" t nil)
+
+(autoload 'org2blog/wp-preview-buffer-post "org2blog" "\
+Preview the present buffer in browser, if posted.
+
+\(fn)" t nil)
+
+(autoload 'org2blog/wp-preview-subtree-post "org2blog" "\
+Preview the present subtree in browser, if posted.
+
+\(fn)" t nil)
+
+;;;***
+
+(provide 'org2blog-autoloads)
View
4 org2blog-pkg.el
@@ -0,0 +1,4 @@
+(define-package "org2blog" "0.5"
+ "Blog from Org mode to wordpress"
+ '((org "7.7")
+ (xml-rpc "1.6.8")))
View
1,253 org2blog.el
@@ -1,7 +1,16 @@
;;; org2blog.el --- blog from Org mode to wordpress
-;; Copyright (C) 2010 Puneeth Chaganti
-;; Author: Puneeth Chaganti <punchagan+org2blog at gmail dot com>
+;; Copyright (C) 2010 Benjamin Beckwith <bnbeckwith@gmail.com>
+;; Copyright (C) 2010 Marcel van der Boom <marcel@hsdev.com>
+;; Copyright (C) 2010,2011 Puneeth Chaganti <punchagan+org2blog@gmail.com>
+;; Copyright (C) 2010 Sacha Chua <sacha@sachachua.com>
+;; Copyright (C) 2010 Giovanni Moretti <Giovanni@reflections.co.nz>
+;; Copyright (C) 2011 Mykola Nikishov <mn@mn.com.ua>
+;; Copyright (C) 2010 Matt Price <matt@roke.mercey.dyndns.org>
+
+;; Author: Puneeth Chaganti <punchagan+org2blog@gmail.com>
+;; Version: 0.5
+;; Keywords: orgmode, wordpress, blog
;; 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
@@ -16,7 +25,7 @@
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-;; A portion of the code in this file is based on blog.el posted to
+;; A portion of the code in this file is based on blog.el posted to
;; http://www.mail-archive.com/gnu-emacs-sources@gnu.org/msg01576.html
;; copyrighted by Ashish Shukla. The Copyright notice from that file is
;; given below.
@@ -43,475 +52,1067 @@
(require 'xml-rpc)
(require 'metaweblog)
-(defgroup org2blog nil
- "Post to weblogs from Emacs"
- :group 'org2blog)
+(defgroup org2blog/wp nil
+ "Post to weblogs from Emacs"
+ :group 'org2blog/wp)
-(defcustom org2blog-server-url nil
- "Weblog XML-RPC URL"
- :group 'org2blog
- :type 'string)
+(defcustom org2blog/wp-blog-alist nil
+ "Association list to set information for each blog.
+Each element of the alist is a blog name. The CAR of each
+element is a string, uniquely identifying the project. The CDR
+of each element is a well-formed property list with an even
+number of elements, alternating keys and values, specifying
+parameters for the blog.
-(defcustom org2blog-server-user nil
- "Weblog server username"
- :group 'org2blog
- :type 'string)
+ (:property value :property value ... )
-(defcustom org2blog-server-pass nil
- "Weblog server password. If this is nil you'll be prompted."
- :group 'org2blog
- :type 'string)
+When a property is given a value in org2blog/wp-blog-alist, its
+setting overrides the value of the corresponding user
+variable (if any) during publishing.
-(defcustom org2blog-server-weblog-id nil
- "Weblog ID"
- :group 'org2blog
- :type 'string)
+Most properties are optional, but some should always be set:
+
+ :url xmlrpc url of the blog.
+ :username username to be used.
+
+All the other properties are optional. They over-ride the global variables.
+
+ :password password to be used
+ :default-title `org2blog/wp-default-title'
+ :default-categories `org2blog/wp-default-categories'
+ Use a list of categories.
+ (\"category1\" \"category2\" ...)
+ :tags-as-categories `org2blog/wp-use-tags-as-categories'
+ :confirm `org2blog/wp-confirm-post'
+ :show `org2blog/wp-show-post-in-browser'
+ :keep-new-lines `org2blog/wp-keep-new-lines'
+ :wp-latex `org2blog/wp-use-wp-latex'
+ :wp-code `org2blog/wp-use-sourcecode-shortcode'
+ :track-posts `org2blog/wp-track-posts'
+"
+ :group 'org2blog/wp
+ :type '(alist :value-type plist))
-(defcustom org2blog-default-categories '("Uncategorized")
- "Default list of categories"
- :group 'org2blog
+(defcustom org2blog/wp-default-categories '("Uncategorized" "Hello")
+ "Default list of categories"
+ :group 'org2blog/wp
:type '(repeat string))
-(defcustom org2blog-default-title "Hello, World"
- "Title of the new post"
- :group 'org2blog
+(defcustom org2blog/wp-buffer-template
+ "#+DATE: %s
+#+OPTIONS: toc:nil num:nil todo:nil pri:nil tags:nil ^:nil TeX:nil
+#+CATEGORY: %s
+#+TAGS:
+#+DESCRIPTION:
+#+TITLE: %s
+\n"
+ "The default template to be inserted in a new post buffer."
+ :group 'org2blog/wp
:type 'string)
-(defcustom org2blog-use-tags-as-categories nil
+(defcustom org2blog/wp-buffer-template-prefix nil
+ "A prefix to the default template used for a new post buffer."
+ :group 'org2blog/wp
+ :type 'string)
+
+(defcustom org2blog/wp-buffer-format-function 'org2blog/wp-format-buffer
+ "Function formatting a buffer according to `org2blog/wp-buffer-template'."
+ :group 'org2blog/wp
+ :type 'function)
+
+(defcustom org2blog/wp-default-title "Hello, World"
+ "Title of the new post"
+ :group 'org2blog/wp
+ :type 'string)
+
+(defcustom org2blog/wp-use-tags-as-categories nil
"Non-nil means assign :tags: to Wordpress categories instead."
- :group 'org2blog
+ :group 'org2blog/wp
+ :type 'boolean)
+
+(defcustom org2blog/wp-confirm-post nil
+ "Non-nil means confirm before Publishing a post."
+ :group 'org2blog/wp
:type 'boolean)
-(defvar org2blog-categories-list nil
+(defcustom org2blog/wp-show-post-in-browser 'ask
+ "A variable to configure if you want to view your post/draft in
+the browser. Setting it to 'ask will prompt you before opening
+it in the browser. Setting it to 'show will show it without
+prompting. Set it to nil, to turn off viewing posts in the
+browser."
+ :group 'org2blog/wp
+ :type 'boolean)
+
+(defcustom org2blog/wp-keep-new-lines nil
+ "Non-nil means do not strip newlines."
+ :group 'org2blog/wp
+ :type 'boolean)
+
+(defcustom org2blog/wp-use-sourcecode-shortcode nil
+ "Non-nil means convert <pre> tags to WP sourcecode blocks.
+NOTE: htmlize.el available in org-mode's contrib directory should
+be on your emacs load-path for this to work."
+ :group 'org2blog/wp
+ :type 'boolean)
+
+(defcustom org2blog/wp-use-wp-latex t
+ "Non-nil means convert LaTeX to WP latex blocks."
+ :group 'org2blog/wp
+ :type 'boolean)
+
+(defcustom org2blog/wp-sourcecode-default-params "light=\"true\""
+ "Default arguments to pass to WP syntaxhighlighter."
+ :group 'org2blog/wp
+ :type 'string)
+
+(defcustom org2blog/wp-sourcecode-langs
+ (list "actionscript3" "bash" "coldfusion" "cpp" "csharp" "css" "delphi"
+ "erlang" "fsharp" "diff" "groovy" "javascript" "java" "javafx" "matlab"
+ "objc" "perl" "php" "text" "powershell" "python" "ruby" "scala" "sql"
+ "vb" "xml")
+ "List of languages supported by sourcecode shortcode of WP."
+ :group 'org2blog/wp
+ :type 'list)
+
+
+(defcustom org2blog/wp-shortcode-langs-map
+ (list
+ '("R" . "r")
+ '("emacs-lisp" . "lisp"))
+ "Association list for source code languages supported by Org
+and by SyntaxHighlighter. Each element of the list maps the
+orgmode source code language (key) to the language spec that
+should be used for syntax highlighting in shortcode blocks. The
+target languages need to be in 'org2blog/wp-sourcecode-langs ."
+ :group 'org2blog/wp
+ :type '(alist :key-type string :value-type string))
+
+(defcustom org2blog/wp-track-posts
+ (list ".org2blog.org" "Posts")
+ "File where to save logs about posts.
+Set to nil if you don't wish to track posts."
+ :group 'org2blog/wp
+ :type 'list)
+
+(defvar org2blog/wp-blog nil
+ "Parameters of the currently selected blog.")
+
+(defvar org2blog/wp-blog-name nil
+ "Name of the blog, to pick from `org2blog/wp-blog-alist'")
+
+(defvar org2blog/wp-categories-list nil
"List of weblog categories")
-(defvar org2blog-tags-list nil
+(defvar org2blog/wp-tags-list nil
"List of weblog tags")
-(defvar org2blog-server-xmlrpc-url nil
+(defvar org2blog/wp-pages-list nil
+ "List of WP pages.")
+
+(defvar org2blog/wp-server-xmlrpc-url nil
"Weblog server XML-RPC URL")
-(defvar org2blog-server-userid nil
+(defvar org2blog/wp-server-userid nil
"Weblog server user id")
-(defvar org2blog-server-blogid nil
+(defvar org2blog/wp-server-blogid nil
"Weblog ID")
-(defvar org2blog-entry-mode-map nil
+(defvar org2blog/wp-entry-mode-map nil
"Keymap for blog entry buffer")
-(defvar org2blog-mode nil
- "Mode for org2blog")
-(make-variable-buffer-local 'org2blog-mode)
-
-(defvar org2blog-logged-in nil
+(defvar org2blog/wp-logged-in nil
"Flag whether user is logged-in or not")
-(defvar org2blog-buffer-name "*org2blog*"
+(defvar org2blog/wp-buffer-name "*org2blog/wp-%s*"
"Name of the blog buffer")
-(defvar org2blog-buffer-kill-prompt t
+(defvar org2blog/wp-buffer-kill-prompt t
"Ask before killing buffer")
-(make-variable-buffer-local 'org2blog-buffer-kill-prompt)
+(make-variable-buffer-local 'org2blog/wp-buffer-kill-prompt)
-(defvar org2blog-track-posts ".org2blog.org"
- "File where to save logs about posts.
-Set to nil if you don't wish to track posts.")
-
-(defconst org2blog-version "0.2"
+(defconst org2blog/wp-version "0.5"
"Current version of blog.el")
-(defun org2blog-kill-buffer-hook ()
+(defvar org2blog/wp-mode-hook nil
+ "Hook to run upon entry into mode.")
+
+(defun org2blog/wp-is-narrow-p nil
+ "Return t if a buffer is narrowed"
+ (not (equal (- (point-max) (point-min)) (buffer-size))))
+
+(defun org2blog/wp-kill-buffer-hook ()
"Prompt before killing buffer."
- (if (and org2blog-buffer-kill-prompt
+ (if (and org2blog/wp-buffer-kill-prompt
(not (buffer-file-name)))
(if (y-or-n-p "Save entry?")
(progn
(save-buffer)
- (if org2blog-track-posts
- (org2blog-save-details (org2blog-parse-entry) nil
- (y-or-n-p "Published?")))))))
+ (org2blog/wp-save-details (org2blog/wp-parse-entry) nil
+ (y-or-n-p "Published?"))))))
-(defun org2blog-mode (&optional arg)
- "org2blog mode for providing mode-map."
- (interactive "P")
- (setq org2blog-mode
- (if (null arg)
- (not org2blog-mode)
- (> (prefix-numeric-value arg) 0)))
- (if org2blog-mode
- (use-local-map org2blog-entry-mode-map)))
-
-(unless org2blog-entry-mode-map
- (setq org2blog-entry-mode-map
- (let ((org2blog-map (make-sparse-keymap)))
- (set-keymap-parent org2blog-map org-mode-map)
- (define-key org2blog-map (kbd "C-c p") (lambda() (interactive) (org2blog-post-entry t)))
- (define-key org2blog-map (kbd "C-c P") (lambda() (interactive) (org2blog-post-entry-as-page t)))
- (define-key org2blog-map (kbd "C-c d") 'org2blog-post-entry)
- (define-key org2blog-map (kbd "C-c D") 'org2blog-post-entry-as-page)
- (define-key org2blog-map (kbd "C-c t") 'org2blog-complete-category)
- org2blog-map)))
-
-(defun org2blog-create-categories (categories)
- "Create unknown CATEGORIES."
+;; Set the mode map for org2blog.
+(unless org2blog/wp-entry-mode-map
+ (setq org2blog/wp-entry-mode-map
+ (let ((org2blog/wp-map (make-sparse-keymap)))
+ (set-keymap-parent org2blog/wp-map org-mode-map)
+ (define-key org2blog/wp-map (kbd "C-c p") 'org2blog/wp-post-buffer-and-publish)
+ (define-key org2blog/wp-map (kbd "C-c P") 'org2blog/wp-post-buffer-as-page-and-publish)
+ (define-key org2blog/wp-map (kbd "C-c d") 'org2blog/wp-post-buffer)
+ (define-key org2blog/wp-map (kbd "C-c D") 'org2blog/wp-post-buffer-as-page)
+ (define-key org2blog/wp-map (kbd "C-c t") 'org2blog/wp-complete-category)
+ org2blog/wp-map)))
+
+;;;###autoload
+(define-minor-mode org2blog/wp-mode
+ "Toggle org2blog/wp mode.
+With no argument, the mode is toggled on/off.
+Non-nil argument turns mode on.
+Nil argument turns mode off.
+
+Commands:
+\\{org2blog/wp-entry-mode-map}
+
+Entry to this mode calls the value of `org2blog/wp-mode-hook'."
+
+ :init-value nil
+ :lighter " o2b"
+ :group 'org2blog/wp
+ :keymap org2blog/wp-entry-mode-map
+
+ (if org2blog/wp-mode
+ (run-mode-hooks 'org2blog/wp-mode-hook)))
+
+(defun org2blog/wp-create-categories (categories)
+ "Prompt and create new categories on WordPress."
(mapcar
(lambda (cat)
- (if (and (not (member cat org2blog-categories-list))
- (y-or-n-p (format "Create %s category? " cat)))
- (wp-new-category org2blog-server-xmlrpc-url
- org2blog-server-userid
- (org2blog-password)
- org2blog-server-blogid
- cat)))
+ (if (and (not (member cat org2blog/wp-categories-list))
+ (y-or-n-p (format "Create '%s' category? " cat)))
+ (wp-new-category org2blog/wp-server-xmlrpc-url
+ org2blog/wp-server-userid
+ org2blog/wp-server-pass
+ org2blog/wp-server-blogid
+ cat))
+ (add-to-list 'org2blog/wp-categories-list cat))
categories))
-(defun org2blog-password ()
- "Get password or prompt if needed."
- (or org2blog-server-pass
- (setq org2blog-server-pass (read-passwd "Weblog password? "))))
+(defun org2blog/wp-password ()
+ "Prompt for, and set password."
+ (interactive)
+ (setq org2blog/wp-server-pass (read-passwd "Weblog password? ")))
+
+(defun org2blog/wp-get-blog-name ()
+ "Get the blog name from a post -- buffer or subtree.
+NOTE: Checks for subtree only when buffer is narrowed."
+ (let ((blog-name
+ (if (org2blog/wp-is-narrow-p)
+ (or (org-entry-get (point) "BLOG") "")
+ (or (org2blog/wp-get-option "blog") ""))))
+ (or (and (assoc blog-name org2blog/wp-blog-alist) blog-name) nil)))
-(defun org2blog-login()
+(defun org2blog/wp-correctly-login ()
+ "Relogin to correct blog, if blog-name is found and different
+from currently logged in."
+ (let ((blog-name (org2blog/wp-get-blog-name)))
+ (when (and blog-name (not (equal blog-name org2blog/wp-blog-name)))
+ (org2blog/wp-logout))
+ (unless org2blog/wp-logged-in
+ (org2blog/wp-login blog-name))))
+
+;;;###autoload
+(defun org2blog/wp-login (&optional blog-name)
"Logs into the blog. Initializes the internal data structures."
(interactive)
- (let ((password))
- (setq org2blog-server-xmlrpc-url (or org2blog-server-url
- (read-no-blanks-input
- "Weblog XML-RPC URL ? ")))
- (setq org2blog-server-userid (or org2blog-server-user
- (read-no-blanks-input
- "Weblog User ID ? ")))
- (setq org2blog-server-blogid (or org2blog-server-weblog-id
- (read-no-blanks-input "Weblog ID ? ")))
- (setq org2blog-categories-list
+ (if (not org2blog/wp-blog-alist)
+ (error "Set `org2blog/wp-blog-alist' to be able to use org2blog."))
+ (let ()
+ (setq org2blog/wp-blog-name
+ (or
+ ;; Use the provided name
+ blog-name
+ ;; OR Use the only entry in alist
+ (and (equal (length org2blog/wp-blog-alist) 1)
+ (car (car org2blog/wp-blog-alist)))
+ ;; OR Prompt user
+ (completing-read
+ "Blog to login into? ([Tab] to see list): "
+ (mapcar 'car org2blog/wp-blog-alist) nil t)))
+ (unless (> (length org2blog/wp-blog-name) 1)
+ (error "Invalid blog name"))
+ (setq org2blog/wp-blog (assoc org2blog/wp-blog-name org2blog/wp-blog-alist)
+ org2blog/wp-server-xmlrpc-url (plist-get (cdr org2blog/wp-blog) :url)
+ org2blog/wp-server-userid (eval (plist-get (cdr org2blog/wp-blog) :username))
+ org2blog/wp-server-blogid (or (plist-get (cdr org2blog/wp-blog) :id) "1")
+ org2blog/wp-server-pass
+ (or
+ (eval (plist-get (cdr org2blog/wp-blog) :password))
+ (read-passwd (format "%s Weblog password? " org2blog/wp-blog-name)))
+ ;; Fetch and save category list
+ org2blog/wp-categories-list
(mapcar (lambda (category) (cdr (assoc "categoryName" category)))
- (metaweblog-get-categories org2blog-server-xmlrpc-url
- org2blog-server-userid
- (org2blog-password)
- org2blog-server-weblog-id)))
- (setq org2blog-tags-list
+ (metaweblog-get-categories org2blog/wp-server-xmlrpc-url
+ org2blog/wp-server-userid
+ org2blog/wp-server-pass
+ org2blog/wp-server-blogid))
+ ;; Fetch and save tag list
+ org2blog/wp-tags-list
(mapcar (lambda (tag) (cdr (assoc "slug" tag)))
- (wp-get-tags org2blog-server-xmlrpc-url
- org2blog-server-userid
- (org2blog-password)
- org2blog-server-weblog-id)))
- (setq org2blog-logged-in t)
+ (wp-get-tags org2blog/wp-server-xmlrpc-url
+ org2blog/wp-server-userid
+ org2blog/wp-server-pass
+ org2blog/wp-server-blogid))
+ ;; Fetch and save page list
+ org2blog/wp-pages-list
+ (mapcar (lambda (pg)
+ (cons (cdr (assoc "page_title" pg))
+ (cdr (assoc "page_id" pg))))
+ (wp-get-pagelist org2blog/wp-server-xmlrpc-url
+ org2blog/wp-server-userid
+ org2blog/wp-server-pass
+ org2blog/wp-server-blogid)))
+ (setq org2blog/wp-logged-in t)
(message "Logged in")))
-(defun org2blog-logout()
+(defun org2blog/wp-logout()
"Logs out from the blog and clears. Clears the internal data structures."
(interactive)
- (setq org2blog-server-xmlrpc-url nil
- org2blog-server-userid nil
- org2blog-server-blogid nil
- org2blog-categories-list nil
- org2blog-logged-in nil)
+ (setq org2blog/wp-server-xmlrpc-url nil
+ org2blog/wp-server-userid nil
+ org2blog/wp-server-blogid nil
+ org2blog/wp-server-pass nil
+ org2blog/wp-categories-list nil
+ org2blog/wp-tags-list nil
+ org2blog/wp-pages-list nil
+ org2blog/wp-logged-in nil)
(message "Logged out"))
-(defun org2blog-new-entry()
- "Creates a new blog entry. Use DESCRIPTION option for categories and KEYWORDS for tags."
+;;;###autoload
+(defun org2blog/wp-new-entry ()
+ "Creates a new buffer for a blog entry."
(interactive)
- (unless org2blog-logged-in
- (org2blog-login))
- (let ((org2blog-buffer (generate-new-buffer org2blog-buffer-name)))
- (switch-to-buffer org2blog-buffer)
- (add-hook 'kill-buffer-hook 'org2blog-kill-buffer-hook nil 'local)
+ ;; Prompt for login
+ (if (and (not org2blog/wp-logged-in)
+ (y-or-n-p "You are not logged in. Login?"))
+ (org2blog/wp-login))
+
+ ;; Generate new buffer
+ (let ((org2blog/wp-buffer (generate-new-buffer
+ (format org2blog/wp-buffer-name org2blog/wp-blog-name))))
+ (switch-to-buffer org2blog/wp-buffer)
+ (add-hook 'kill-buffer-hook 'org2blog/wp-kill-buffer-hook nil 'local)
(org-mode)
- (insert "#+DATE: ")
- (insert (format-time-string "[%Y-%m-%d %a %H:%M]\n" (current-time)))
- (insert "#+OPTIONS: toc:nil num:nil todo:nil pri:nil tags:nil ^:{}\n")
- (insert "#+DESCRIPTION: \n")
- (insert "#+KEYWORDS: \n")
- (insert "#+TITLE: <Enter Title Here>")
- (newline)
- (use-local-map org2blog-entry-mode-map)))
-
-(defun upload-images-insert-links ()
- "Uploads images if any in the html, and changes their links"
+ ;; Insert the post template
+ (insert
+ (or org2blog/wp-buffer-template-prefix "")
+ (funcall org2blog/wp-buffer-format-function
+ org2blog/wp-buffer-template))
+ (org2blog/wp-mode t)))
+
+(defun org2blog/wp-format-buffer (buffer-template)
+ "Default buffer formatting function."
+ (format buffer-template
+ (format-time-string "[%Y-%m-%d %a %H:%M]" (current-time))
+ (mapconcat
+ (lambda (cat) cat)
+ (or (plist-get (cdr org2blog/wp-blog) :default-categories)
+ org2blog/wp-default-categories)
+ ", ")
+ (or (plist-get (cdr org2blog/wp-blog) :default-title)
+ org2blog/wp-default-title)))
+
+(defun org2blog/wp-upload-files-replace-urls (text)
+ "Uploads files, if any in the html, and changes their links"
(let ((file-all-urls nil)
- file-name file-web-url blog-pass
- (image-regexp (org-image-file-name-regexp)))
+ file-name file-web-url beg
+ (file-regexp "<a href=\"\\(.?*\\)\"\\|<img src=\"\\(.*?\\)\""))
(save-excursion
- (goto-char (point-min))
- (while (re-search-forward org-any-link-re nil t 1)
- (let ((link (or (match-string-no-properties 2) (match-string-no-properties 0))))
- (if (and
- (save-match-data (string-match image-regexp link))
- (save-match-data (not (string-match org-link-types-re link))))
+ (while (string-match file-regexp text beg)
+ (setq file-name
+ (if (match-beginning 1)
+ (substring text (match-beginning 1) (match-end 1))
+ (substring text (match-beginning 2) (match-end 2))))
+ (setq file-name (save-match-data (if (string-match "^file:" file-name)
+ (substring file-name 7)
+ file-name)))
+ (setq beg (match-end 0))
+ (if (save-match-data (not (or
+ (string-match org-plain-link-re file-name)
+ (string-match "^#" file-name)
+ (string-equal (file-name-nondirectory file-name) ""))))
+
(progn
- (setq file-name (match-string-no-properties 2))
- (setq file-web-url
- (cdr (assoc "url"
- (metaweblog-upload-image org2blog-server-xmlrpc-url
- org2blog-server-userid
- (or org2blog-server-pass
- blog-pass
- (setq blog-pass (read-passwd
- (format
- "%s image upload - blog password ? "
- file-name))))
- org2blog-server-weblog-id
- (get-image-properties file-name)))))
- (setq file-all-urls (append file-all-urls (list (cons
- file-name file-web-url))))))))
- (goto-char (point-min))
- (dolist (image file-all-urls)
- (replace-string (car image) (cdr image))))))
+ (goto-char (point-min))
+ (if (re-search-forward (concat "^#\\+"
+ (regexp-quote file-name)
+ " ") nil t 1)
+ (setq file-web-url (buffer-substring-no-properties
+ (point)
+ (or (end-of-line) (point))))
+ (setq file-web-url
+ (cdr (assoc "url"
+ (metaweblog-upload-file
+ org2blog/wp-server-xmlrpc-url
+ org2blog/wp-server-userid
+ org2blog/wp-server-pass
+ org2blog/wp-server-blogid
+ (get-file-properties file-name)))))
+ (goto-char (point-max))
+ (newline)
+ (insert (concat "#+" file-name " " file-web-url)))
+ (setq file-all-urls
+ (append file-all-urls (list (cons
+ file-name file-web-url)))))))
+ (dolist (file file-all-urls)
+ (setq text (replace-regexp-in-string
+ (concat "\\(<a href=\"\\|<img src=\"\\)\\(file://\\)*" (regexp-quote (car file)))
+ (concat "\\1" (cdr file)) text))))
+ text))
-(defun org2blog-get-post-id ()
- "Gets the post-id from a buffer."
- (let (post-id)
- (save-excursion
+(defun org2blog/wp-get-option (opt)
+ "Gets an the value of the option OP from a buffer."
+ (let* ((r (org-make-options-regexp (list (upcase opt) (downcase opt)))))
+ (save-excursion
(goto-char (point-min))
- (if (re-search-forward "^#\\+POSTID: \\(.*\\)" nil t 1)
- (setq post-id (match-string-no-properties 1))))
- post-id))
+ (if (re-search-forward r nil t 1)
+ (match-string-no-properties 2)))))
-(defun org2blog-strip-new-lines (html)
+(defun org2blog/wp-get-post-parent (post-par)
+ "Gets the post's parent from a buffer."
+ (if post-par
+ (or (cdr (assoc
+ (car (split-string post-par "\\( *, *\\)" t))
+ org2blog/wp-pages-list))
+ "0")
+ "0"))
+
+(defun org2blog/wp-strip-new-lines (html)
"Strip the new lines from the html, except in pre and blockquote tags."
(save-excursion
(with-temp-buffer
- (let* (start-pos end-pos)
+ (let* (start-pos end-pos html-tag)
(insert html)
(setq start-pos (point-min))
(goto-char start-pos)
- (while (re-search-forward "<\\(pre\\|blockquote\\).*?>" nil t 1)
+ ;; Search for pre or blockquote start
+ (while (re-search-forward
+ "<\\(pre\\|blockquote\\).*?>"
+ nil t 1)
(setq end-pos (match-beginning 0))
+ (setq html-tag (match-string-no-properties 1))
+ ;; Replace all new lines before the start
(replace-regexp "\\\n" " " nil start-pos end-pos)
- (re-search-forward (concat "</" (match-string-no-properties 1) ">") nil t 1)
+ ;; Go to the end of the pre or blockquote block, and start again
+ (re-search-forward (format "</%s.*?>" html-tag) nil t 1)
(setq start-pos (match-end 0))
(goto-char start-pos))
(setq end-pos (point-max))
(replace-regexp "\\\n" " " nil start-pos end-pos)
(buffer-substring-no-properties (point-min) (point-max))))))
-(defun org2blog-parse-entry (&optional publish)
- "Parse an org2blog buffer."
+(defun org2blog/wp-point-in-wp-sc ()
+ "Return True when point in sourcecode block."
+ (save-excursion
+ (let* ((pos (point))
+ (s-count 0)
+ (e-count 0))
+ (save-match-data
+ (while (re-search-backward "\\[sourcecode.*\\]" nil t)
+ (setq s-count (1+ s-count)))
+ (goto-char pos)
+ (while (re-search-backward "\\[/sourcecode\\]" nil t)
+ (setq e-count (1+ e-count))))
+ (> (- s-count e-count) 0))))
+
+(defun org2blog/wp-latex-to-wp (html)
+ "Change inline LaTeX to wp latex blocks."
+ (save-excursion
+ (with-temp-buffer
+ (insert html)
+ (let* ((matchers (plist-get org-format-latex-options :matchers))
+ (re-list org-latex-regexps)
+ beg end re e m n block off)
+ (while (setq e (pop re-list))
+ (setq m (car e) re (nth 1 e) n (nth 2 e)
+ block (if (nth 3 e) "\n\n" ""))
+ (when (member m matchers)
+ (goto-char (point-min))
+ (save-match-data
+ (while (and
+ (re-search-forward re nil t)
+ (not (org2blog/wp-point-in-wp-sc)))
+ (cond
+ ((equal m "$")
+ (unless (string-match "^latex" (match-string 4))
+ (replace-match (concat (match-string 1) "$latex "
+ (match-string 4) "$"
+ (match-string 6))
+ nil t)))
+ ((equal m "$1")
+ (replace-match (concat (match-string 1) "$latex "
+ (substring (match-string 2) 1 -1)
+ "$" (match-string 3))
+ nil t))
+ ((equal m "\\(")
+ (replace-match (concat "$latex "
+ (substring (match-string 0) 2 -2)
+ "$") nil t))
+ ((equal m "\\[")
+ (replace-match (concat "<p style=\"text-align:center\"> $latex \\displaystyle "
+ (substring (match-string 0) 2 -2)
+ "$ </p>") nil t))
+ ((equal m "$$")
+ (replace-match (concat "<p style=\"text-align:center\"> $latex \\displaystyle "
+ (substring (match-string 0) 2 -2)
+ "$ </p>") nil t))
+ ((equal m "begin")
+ (if (equal (match-string 2) "equation")
+ (replace-match (concat "<p style=\"text-align:center\"> $latex \\displaystyle "
+ (substring (match-string 1) 16 -14)
+ "$ </p>") nil t)))))))))
+ (buffer-substring-no-properties (point-min) (point-max)))))
+
+(defun org2blog/wp-replace-pre (html)
+ "Replace pre blocks with sourcecode shortcode blocks."
+ (save-excursion
+ (let (pos code lang info params header code-start code-end html-attrs)
+ (with-temp-buffer
+ (insert html)
+ (goto-char (point-min))
+ (save-match-data
+ (while (re-search-forward "<pre\\(.*?\\)>" nil t 1)
+
+ ;; When the codeblock is a src_block
+ (unless
+ (save-match-data
+ (string-match "example" (match-string-no-properties 1)))
+ ;; Replace the <pre...> text
+ (replace-match "")
+ (setq code-start (point))
+
+ ;; Go to end of code and remove </pre>
+ (re-search-forward "</pre.*?>" nil t 1)
+ (replace-match "")
+ (setq code-end (point))
+ (setq code (buffer-substring-no-properties code-start code-end))
+
+ ;; Delete the code
+ (delete-region code-start code-end)
+ ;; Stripping out all the code highlighting done by htmlize
+ (setq code (replace-regexp-in-string "<.*?>" "" code))
+ (insert (concat "\n[sourcecode]\n" code "[/sourcecode]\n")))))
+
+ ;; Get the new html!
+ (setq html (buffer-substring-no-properties (point-min) (point-max))))
+
+ (setq pos (point-min))
+ (goto-char (point-min))
+ (while
+ ;; Search for code blocks in the buffer from where publish
+ ;; was done, and get the syntaxhl params if any, and put
+ ;; them in the right place in the html, using a temp-buffer.
+ (save-match-data
+ (re-search-forward org-babel-src-block-regexp nil t 1))
+ (backward-word)
+ ;; Get the syntaxhl params and other info about the src_block
+ (let* ((info (org-babel-get-src-block-info))
+ (params (nth 2 info))
+ (code (org-html-protect (nth 1 info)))
+ (org-src-lang
+ (or (cdr (assoc (nth 0 info) org2blog/wp-shortcode-langs-map))
+ (nth 0 info)))
+ (lang (or (car
+ (member org-src-lang org2blog/wp-sourcecode-langs))
+ "text"))
+ (header (concat "[sourcecode language=\"" lang "\" "
+ (if (assoc :syntaxhl params)
+ (cdr (assoc :syntaxhl params))
+ org2blog/wp-sourcecode-default-params)
+ "]")))
+
+ ;; Change the html by inserting the syntaxhl in the right place.
+ (save-excursion
+ (with-temp-buffer
+ (insert html)
+ (goto-char pos)
+ ;; Search for code
+ (save-match-data
+ (search-forward code nil t 1)
+ (setq pos (match-end 0))
+ (goto-char (match-beginning 0))
+ ;; Search for a header line --
+ (search-backward "[sourcecode]" nil t 1)
+ ;; Replace the text with our new header
+ (replace-match header nil t))
+ (setq html (buffer-substring-no-properties (point-min) (point-max)))))))))
+ html)
+
+(defun org2blog/wp-parse-entry (&optional publish)
+ "Parse an org2blog/wp buffer."
(interactive "P")
- (let* (html-text post-title post-id post-buffer post-date tags categories narrow-p cur-time)
+ (let* ((keep-new-lines (if (plist-member (cdr org2blog/wp-blog) :keep-new-lines)
+ (plist-get (cdr org2blog/wp-blog) :keep-new-lines)
+ org2blog/wp-keep-new-lines))
+ (wp-latex (if (plist-member (cdr org2blog/wp-blog) :wp-latex)
+ (plist-get (cdr org2blog/wp-blog) :wp-latex)
+ org2blog/wp-use-wp-latex))
+ (sourcecode-shortcode (if (plist-member (cdr org2blog/wp-blog) :wp-code)
+ (plist-get (cdr org2blog/wp-blog) :wp-code)
+ org2blog/wp-use-sourcecode-shortcode))
+ html-text post-title post-id post-date tags categories narrow-p
+ cur-time post-par)
(save-restriction
(save-excursion
- (setq narrow-p (not (equal (- (point-max) (point-min)) (buffer-size))))
+ (if (not org2blog/wp-mode)
+ (org-save-outline-visibility 'use-markers (org-mode-restart))
+ (org-save-outline-visibility 'use-markers (org-mode-restart))
+ (org2blog/wp-mode t))
+ (setq narrow-p (org2blog/wp-is-narrow-p))
+
+ ;; Get the required parameters for posting the blog-post
(if narrow-p
(progn
- (setq post-title (or (org-entry-get (point) "Title")
+ (setq post-title (or (org-entry-get (point) "TITLE")
(nth 4 (org-heading-components))))
- (setq post-id (org-entry-get (point) "Post ID"))
+ (setq excerpt (org-entry-get (point) "DESCRIPTION"))
+ (setq permalink (org-entry-get (point) "PERMALINK"))
+ (setq post-id (or (org-entry-get (point) "POSTID")
+ (org-entry-get (point) "POST_ID")))
+ (setq post-par (org2blog/wp-get-post-parent
+ (org-entry-get (point) "PARENT")))
;; Set post-date to the Post Date property or look for timestamp
- (setq post-date (or (org-entry-get (point) "Post Date")
+ (setq post-date (or (org-entry-get (point) "POST_DATE")
(org-entry-get (point) "SCHEDULED")
(org-entry-get (point) "DEADLINE")
(org-entry-get (point) "TIMESTAMP_IA")
(org-entry-get (point) "TIMESTAMP")))
(setq tags (mapcar 'org-no-properties (org-get-tags-at (point) nil)))
- (setq categories (org-split-string
- (or (org-entry-get (point) "CATEGORIES") "") ":")))
- (setq post-buffer (buffer-name))
- (setq post-title (plist-get (org-infile-export-plist) :title))
- (setq post-id (org2blog-get-post-id))
+ (setq categories (org-entry-get (point) "CATEGORY"))
+ (setq categories (if categories
+ (split-string categories "\\( *, *\\)" t)
+ "")))
+ (setq post-title (or (plist-get (org-infile-export-plist) :title)
+ "No Title"))
+ (setq excerpt (plist-get (org-infile-export-plist) :description))
+ (setq permalink (org2blog/wp-get-option "PERMALINK"))
+ (setq post-id (org2blog/wp-get-option "POSTID"))
+ (setq post-par (org2blog/wp-get-post-parent
+ (org2blog/wp-get-option "PARENT")))
(setq post-date (plist-get (org-infile-export-plist) :date))
- (setq tags (plist-get (org-infile-export-plist) :keywords))
- (setq categories (plist-get (org-infile-export-plist) :description))
- (setq tags
- (or (split-string (or tags "") "[ ,]+" t) ""))
+ (setq tags (org2blog/wp-get-option "TAGS"))
+ (setq tags (if tags (split-string tags "\\( *, *\\)" t) ""))
- (setq categories
- (or (split-string (or categories "") "[ ,]+" t) "")))
+ (setq categories (org2blog/wp-get-option "CATEGORY"))
+ (setq categories (if categories
+ (split-string categories "\\( *, *\\)" t)
+ "")))
;; Convert post date to ISO timestamp
;;add the date of posting to the post. otherwise edits will change it
- (setq cur-time (format-time-string (org-time-stamp-format t t) (org-current-time) t))
+ (setq cur-time (format-time-string (org-time-stamp-format t t) (org-current-time)))
(setq post-date
- (format-time-string "%Y%m%dT%T"
+ (format-time-string "%Y%m%dT%T%z"
(if post-date
(apply 'encode-time (org-parse-time-string post-date))
(current-time)
(if narrow-p
- (org-entry-put (point) "Post Date" cur-time)
+ (org-entry-put (point) "POST_DATE" cur-time)
(save-excursion
(goto-char (point-min))
(insert (concat "#+DATE: " cur-time "\n")))))
- nil))
-
- (if org2blog-use-tags-as-categories
+ t))
+
+ (if
+ (if (plist-member (cdr org2blog/wp-blog) :tags-as-categories)
+ (plist-get (cdr org2blog/wp-blog) :tags-as-categories)
+ org2blog/wp-use-tags-as-categories)
(setq categories tags
tags nil))
-
- (upload-images-insert-links)
- (if (not narrow-p)
- (setq html-text (org-export-as-html nil nil nil 'string t nil))
- (setq html-text
- (org-export-region-as-html
- (1+ (and (org-back-to-heading) (line-end-position)))
- (org-end-of-subtree)
- t 'string)))
-
- (setq html-text (org2blog-strip-new-lines
- (org-no-properties html-text)))))
+
+ ;; Get the exported html
+ (save-excursion
+ (if (not narrow-p)
+ (setq html-text (org-export-as-html nil nil nil 'string t nil))
+ (setq html-text
+ (org-export-region-as-html
+ (1+ (and (org-back-to-heading) (line-end-position)))
+ (org-end-of-subtree)
+ t 'string)))
+ (setq html-text (org-no-properties html-text)))
+
+ ;; Post-process as required.
+ (setq html-text (org2blog/wp-upload-files-replace-urls html-text))
+ (unless keep-new-lines
+ (setq html-text (org2blog/wp-strip-new-lines html-text)))
+ (when sourcecode-shortcode
+ (setq html-text (org2blog/wp-replace-pre html-text)))
+ (when wp-latex
+ (setq html-text (org2blog/wp-latex-to-wp html-text)))))
(list
(cons "point" (point))
(cons "subtree" narrow-p)
(cons "date" post-date)
- (cons "title" post-title)
+ (cons "title" (org-html-do-expand post-title))
(cons "tags" tags)
(cons "categories" categories)
(cons "post-id" post-id)
- (cons "buffer" post-buffer)
+ (cons "parent" post-par)
+ (cons "excerpt" (org-html-do-expand (or excerpt "")))
+ (cons "permalink" (or permalink ""))
(cons "description" html-text))))
-(defun org2blog-post-entry (&optional publish)
+
+(defun org2blog/wp-post-buffer-and-publish ()
+ "Post buffer and mark it as published"
+ (interactive)
+ (org2blog/wp-post-buffer t))
+
+(defun org2blog/wp-post-buffer (&optional publish)
"Posts new blog entry to the blog or edits an existing entry."
(interactive "P")
- (unless org2blog-logged-in
- (org2blog-login))
- (let ((post (org2blog-parse-entry))
- post-id post-buf)
- (save-excursion
- (org2blog-create-categories (cdr (assoc "categories" post)))
- (setq post-id (cdr (assoc "post-id" post)))
- (setq post-buf (cdr (assoc "buffer" post)))
- (if post-id
- (metaweblog-edit-post org2blog-server-xmlrpc-url
- org2blog-server-userid
- (org2blog-password)
- post-id
- post
- publish)
- (setq post-id (metaweblog-new-post org2blog-server-xmlrpc-url
- org2blog-server-userid
- (org2blog-password)
- org2blog-server-blogid
- post
- publish))
- (if (cdr (assoc "subtree" post))
- (org-entry-put (point) "Post ID" post-id)
- (goto-char (point-min))
- (insert (concat "#+POSTID: " post-id "\n")))))
- (if org2blog-track-posts
- (org2blog-save-details post post-id publish))
- (message (if publish
- "Published (%s): %s"
- "Draft (%s): %s")
- post-id
- (cdr (assoc "title" post)))))
-
-(defun org2blog-post-entry-as-page (&optional publish)
+ (org2blog/wp-mode t) ;; turn on org2blog-wp-mode
+ (org2blog/wp-correctly-login)
+ (save-excursion
+ (save-restriction
+ (let ((post (org2blog/wp-parse-entry))
+ (confirm (and
+ (if (plist-member (cdr org2blog/wp-blog) :confirm)
+ (plist-member (cdr org2blog/wp-blog) :confirm)
+ org2blog/wp-confirm-post)
+ publish))
+ (show (or (plist-member (cdr org2blog/wp-blog) :show)
+ org2blog/wp-show-post-in-browser))
+ post-id)
+ (org2blog/wp-create-categories (cdr (assoc "categories" post)))
+ (setq post-id (cdr (assoc "post-id" post)))
+ (when confirm
+ (if (not (y-or-n-p (format "Publish %s ?"
+ (cdr (assoc "title" post)))))
+ (error "Post cancelled.")))
+ (if post-id
+ (metaweblog-edit-post org2blog/wp-server-xmlrpc-url
+ org2blog/wp-server-userid
+ org2blog/wp-server-pass
+ post-id
+ post
+ publish)
+ (setq post-id (metaweblog-new-post org2blog/wp-server-xmlrpc-url
+ org2blog/wp-server-userid
+ org2blog/wp-server-pass
+ org2blog/wp-server-blogid
+ post
+ publish))
+ (if (cdr (assoc "subtree" post))
+ (progn
+ (org-entry-put (point) "POSTID" post-id)
+ (org-entry-put (point) "BLOG" org2blog/wp-blog-name))
+ (goto-char (point-min))
+ (insert (concat "#+BLOG: " org2blog/wp-blog-name "\n"))
+ (insert (concat "#+POSTID: " post-id "\n"))))
+ (org2blog/wp-save-details post post-id publish)
+ (message (if publish
+ "Published (%s): %s"
+ "Draft (%s): %s")
+ post-id
+ (cdr (assoc "title" post)))
+ (when (or (equal show 'show)
+ (and
+ (equal show 'ask)
+ (y-or-n-p
+ "[For drafts, ensure you login] View in browser? y/n")))
+ (if (cdr (assoc "subtree" post))
+ (org2blog/wp-preview-subtree-post)
+ (org2blog/wp-preview-buffer-post)))))))
+
+
+(defun org2blog/wp-post-buffer-as-page-and-publish ()
+ "Alias to post buffer and mark it as published"
+ (interactive)
+ (org2blog/wp-post-buffer-as-page t))
+
+(defun org2blog/wp-post-buffer-as-page (&optional publish)
"Posts new page to the blog or edits an existing page."
(interactive "P")
- (unless org2blog-logged-in
- (org2blog-login))
- (let ((post (org2blog-parse-entry))
- post-id post-buf)
- (org2blog-create-categories (cdr (assoc "categories" post)))
- (setq post-id (cdr (assoc "post-id" post)))
- (setq post-buf (cdr (assoc "buffer" post)))
- (if post-id
- (metaweblog-edit-post org2blog-server-xmlrpc-url
- org2blog-server-userid
- (org2blog-password)
- post-id
- post
- publish)
- (setq post-id (wp-new-page org2blog-server-xmlrpc-url
- org2blog-server-userid
- (org2blog-password)
- org2blog-server-blogid
- post
- publish))
- (if (cdr (assoc "subtree" post))
- (org-entry-put (point) "Post ID" post-id)
- (save-excursion
- (goto-char (point-min))
- (insert (concat "#+POSTID: " post-id "\n")))))
- (if org2blog-track-posts
- (org2blog-save-details post post-id publish))
- (message (if publish
- "Published (%s): %s"
- "Draft (%s): %s")
- post-id
- (cdr (assoc "title" post)))))
-
-(defun org2blog-delete-entry (&optional post-id)
+ (org2blog/wp-correctly-login)
+ (save-excursion
+ (save-restriction
+ (widen)
+ (let ((post (org2blog/wp-parse-entry))
+ (confirm (and
+ (if (plist-member (cdr org2blog/wp-blog) :confirm)
+ (plist-member (cdr org2blog/wp-blog) :confirm)
+ org2blog/wp-confirm-post)
+ publish))
+ (show (or (plist-member (cdr org2blog/wp-blog) :show)
+ org2blog/wp-show-post-in-browser))
+ post-id)
+ (org2blog/wp-create-categories (cdr (assoc "categories" post)))
+ (setq post-id (cdr (assoc "post-id" post)))
+ (when confirm
+ (if (not (y-or-n-p (format "Publish %s ?"
+ (cdr (assoc "title" post)))))
+ (error "Post cancelled.")))
+ (if post-id
+ (wp-edit-page org2blog/wp-server-xmlrpc-url
+ org2blog/wp-server-userid
+ org2blog/wp-server-pass
+ org2blog/wp-server-blogid
+ post-id
+ post
+ publish)
+ (setq post-id (wp-new-page org2blog/wp-server-xmlrpc-url
+ org2blog/wp-server-userid
+ org2blog/wp-server-pass
+ org2blog/wp-server-blogid
+ post
+ publish))
+ (setq org2blog/wp-pages-list
+ (mapcar (lambda (pg)
+ (cons (cdr (assoc "title" pg))
+ (cdr (assoc "page_id" pg))))
+ (wp-get-pagelist org2blog/wp-server-xmlrpc-url
+ org2blog/wp-server-userid
+ org2blog/wp-server-pass
+ org2blog/wp-server-blogid)))
+ (if (cdr (assoc "subtree" post))
+ (org-entry-put (point) "POSTID" post-id)
+ (goto-char (point-min))
+ (insert (concat "#+POSTID: " post-id "\n"))))
+ (org2blog/wp-save-details post post-id publish)
+ (message (if publish
+ "Published (%s): %s"
+ "Draft (%s): %s")
+ post-id
+ (cdr (assoc "title" post)))
+ (when (or (equal show 'show)
+ (and
+ (equal show 'ask)
+ (y-or-n-p
+ "[For drafts, ensure you login] View in browser? y/n")))
+ (if (cdr (assoc "subtree" post))
+ (org2blog/wp-preview-subtree-post)
+ (org2blog/wp-preview-buffer-post)))))))
+
+(defun org2blog/wp-delete-entry (&optional post-id)
(interactive "P")
+ (org2blog/wp-correctly-login)
(if (null post-id)
- (setq post-id (org2blog-get-post-id)))
- (metaweblog-delete-post org2blog-server-xmlrpc-url
- org2blog-server-userid
- (org2blog-password)
+ (setq post-id (org2blog/wp-get-option "POSTID")))
+ (metaweblog-delete-post org2blog/wp-server-xmlrpc-url
+ org2blog/wp-server-userid
+ org2blog/wp-server-pass
post-id)
(message "Post Deleted"))
-(defun org2blog-delete-page (&optional page-id)
+(defun org2blog/wp-delete-page (&optional page-id)
(interactive "P")
+ (org2blog/wp-correctly-login)
(if (null page-id)
- (setq page-id (org2blog-get-post-id)))
- (wp-delete-page org2blog-server-xmlrpc-url
- org2blog-server-blogid
- org2blog-server-userid
- (org2blog-password)
+ (setq page-id (org2blog/wp-get-option "POSTID")))
+ (wp-delete-page org2blog/wp-server-xmlrpc-url
+ org2blog/wp-server-blogid
+ org2blog/wp-server-userid
+ org2blog/wp-server-pass
page-id)
(message "Page Deleted"))
-(defun org2blog-save-details (post pid pub)
+(defun org2blog/wp-save-details (post pid pub)
"Save the details of posting, to a file."
- (let (o2b-id log-file)
- (save-excursion
- (if (cdr (assoc "subtree" post))
- (setq o2b-id (org-id-get nil t "o2b"))
- (setq o2b-id (buffer-file-name)))
- (setq log-file (find-file (expand-file-name
- org2blog-track-posts org-directory)))
- (if (not o2b-id)
- ()
- (if (not (condition-case nil
- (org-link-search o2b-id)
- (error nil)))
- (progn
- (goto-char (point-max))
- (org-insert-heading-respect-content)
- (insert (cdr (assoc "title" post)))))
- (org-entry-put (point) "Post ID" (or pid ""))
- (org-entry-put (point) "Post Date" (cdr (assoc "date" post)))
- (org-entry-put (point) "Published" (if pub "Yes" "No"))
- (org-entry-put (point) "Where"
- (if (cdr (assoc "subtree" post))
- (concat "[[id:" o2b-id "]]")
- (concat "[[file:" o2b-id "]]"))))
- (save-buffer)
- (kill-buffer log-file))))
-
-(defun org2blog-complete-category()
- "Provides completion for categories and tags. DESCRIPTION for categories and KEYWORDS for tags."
+ (save-excursion
+ (when (if (plist-member (cdr org2blog/wp-blog) :track-posts)
+ (car (plist-get (cdr org2blog/wp-blog) :track-posts))
+ (car org2blog/wp-track-posts))
+ (let* ((o2b-id (if (cdr (assoc "subtree" post))
+ (concat "id:" (org-id-get nil t))
+ (buffer-file-name)))
+ (log-file (if (plist-member (cdr org2blog/wp-blog) :track-posts)
+ (car (plist-get (cdr org2blog/wp-blog) :track-posts))
+ (car org2blog/wp-track-posts)))
+ (log-file (if (file-name-absolute-p log-file)
+ log-file
+ (if org-directory
+ (expand-file-name log-file org-directory)
+ (message "org-track-posts: filename is ambiguous
+use absolute path or set org-directory")
+ log-file)))
+ (headline (if (plist-member (cdr org2blog/wp-blog) :track-posts)
+ (cadr (plist-get (cdr org2blog/wp-blog) :track-posts))
+ (cadr org2blog/wp-track-posts)))
+ p)
+ (when o2b-id
+ (with-current-buffer (or (find-buffer-visiting log-file)
+ (find-file-noselect log-file))
+ (save-excursion
+ (save-restriction
+ (widen)
+ (goto-char (point-min))
+ (show-all)
+ (setq p (org-find-exact-headline-in-buffer headline))
+ (if p
+ (progn (goto-char p) (org-narrow-to-subtree) (end-of-line))
+ (goto-char (point-max))
+ (if (y-or-n-p (format "Heading '%s' not in '%s'; Create?"
+ headline log-file))
+ (progn (org-insert-heading t) (insert headline)
+ (org-narrow-to-subtree))))
+ (if (search-forward o2b-id nil t 1)
+ (progn
+ (org-back-to-heading)
+ (forward-thing 'whitespace)
+ (kill-line))
+ (org-insert-subheading t)))
+ (org2blog/wp-update-details post o2b-id pid pub))
+ (save-buffer)))))))
+
+(defun org2blog/wp-update-details (post o2b-id pid pub)
+ "Inserts details of a new post or updates details."
+ (insert (format "[[%s][%s]]"
+ (if (cdr (assoc "subtree" post))
+ o2b-id
+ (concat "file:" o2b-id))
+ (cdr (assoc "title" post))))
+ (org-entry-put (point) "POSTID" (or pid ""))
+ (org-entry-put (point) "POST_DATE" (cdr (assoc "date" post)))
+ (org-entry-put (point) "Published" (if pub "Yes" "No")))
+
+(defun org2blog/wp-complete-category()
+ "Provides completion for categories and tags."
(interactive)
(let* (current-pos tag-or-category-list)
(setq current-pos (point))
(forward-line 0)
(forward-char 2)
- (if (or (looking-at "DESCRIPTION: ") (looking-at "KEYWORDS: "))
- (progn
- (if (looking-at "KEYWORDS: ")
- (setq tag-or-cat-list org2blog-tags-list)
- (setq tag-or-cat-list org2blog-categories-list))
- (goto-char current-pos)
+ (if (or (looking-at "CATEGORY: ") (looking-at "TAGS: ")
+ (looking-at "PARENT: "))
+ (progn
+ (cond
+ ((looking-at "TAGS: ")
+ (setq tag-or-cat-list org2blog/wp-tags-list)
+ (setq tag-or-cat-prompt "Tag ?"))
+ ((looking-at "CATEGORY: ")
+ (setq tag-or-cat-list org2blog/wp-categories-list)
+ (setq tag-or-cat-prompt "Category ?"))
+ ((looking-at "PARENT: ")
+ (setq tag-or-cat-list org2blog/wp-pages-list)
+ (setq tag-or-cat-prompt "Parent ?")))
+ (goto-char current-pos)
(let ((word-match (or (current-word t) ""))
(completion-match nil))
(when word-match
- (setq completion-match (completing-read "Category ? " tag-or-cat-list nil nil word-match))
+ (setq completion-match (completing-read tag-or-cat-prompt tag-or-cat-list nil nil word-match))
(when (stringp completion-match)
(search-backward word-match nil t)
- (replace-match (concat completion-match ", ") nil t)))))
+ (replace-match (concat completion-match ", ") nil t)))))
(progn
(goto-char current-pos)
(command-execute (lookup-key org-mode-map (kbd "C-c t")))))))
-(defun org2blog-post-subtree (&optional publish)
+;;;###autoload
+(defun org2blog/wp-post-subtree (&optional publish)
"Post the current entry as a draft. Publish if PUBLISH is non-nil."
(interactive "P")
(save-restriction
(save-excursion
+ (org-insert-heading-after-current)
+ (if (fboundp 'org-backward-heading-same-level)
+ (org-backward-heading-same-level 1)
+ (org-backward-same-level 1))
(org-narrow-to-subtree)
- (org2blog-post-entry publish)
+ (org-id-get nil t "o2b")
+ (goto-char (point-min))
+ (setq level (1- (org-reduced-level (org-outline-level))))
+ (dotimes (n level nil) (org-promote-subtree))
+ (org2blog/wp-post-buffer publish)
+ (dotimes (n level nil) (org-demote-subtree))
+ (widen)
+ (if (fboundp 'org-forward-heading-same-level)
+ (org-forward-heading-same-level 1)
+ (org-forward-same-level 1))
+ (delete-region (or (beginning-of-line) (point))
+ (1+ (or (end-of-line) (point))))
+ (save-buffer))))
+
+;;;###autoload
+(defun org2blog/wp-track-buffer ()
+ "Save details of current buffer in the tracking file."
+ (interactive)
+ (save-restriction
+ (save-excursion
+ (widen)
+ (org2blog/wp-save-details (org2blog/wp-parse-entry) "" nil))))
+
+;;;###autoload
+(defun org2blog/wp-track-subtree ()
+ "Save details of current subtree in the tracking file."
+ (interactive)
+ (save-restriction
+ (save-excursion
+ (org-narrow-to-subtree)
+ (org2blog/wp-save-details (org2blog/wp-parse-entry) "" nil)
(widen))))
+;;;###autoload
+(defun org2blog/wp-preview-buffer-post ()
+ "Preview the present buffer in browser, if posted."
+ (interactive)
+ (org2blog/wp-correctly-login)
+ (let* ((postid (org2blog/wp-get-option "POSTID"))
+ (url org2blog/wp-server-xmlrpc-url))
+ (if (not postid)
+ (message "This buffer hasn't been posted, yet.")
+ (setq url (substring url 0 -10))
+ (setq url (concat url "?p=" postid "&preview=true"))
+ (browse-url url))))
+
+;;;###autoload
+(defun org2blog/wp-preview-subtree-post ()
+ "Preview the present subtree in browser, if posted."
+ (interactive)
+ (org-narrow-to-subtree)
+ (org2blog/wp-correctly-login)
+ (widen)
+ (let* ((postid (or (org-entry-get (point) "POSTID")
+ (org-entry-get (point) "POST_ID")))
+ (url org2blog/wp-server-xmlrpc-url))
+ (if (not postid)
+ (message "This subtree hasn't been posted, yet.")
+ (setq url (substring url 0 -10))
+ (setq url (concat url "?p=" postid "&preview=true"))
+ (browse-url url))))
+
+(defun org2blog/wp-insert-post-or-page-link (&optional is-page)
+ "Insert a link to the post (or page) with the given id, with
+the title of the post (or page) as description."
+ (interactive "P")
+ (org2blog/wp-correctly-login)
+ (let* ((post-list (if is-page
+ (wp-get-pagelist org2blog/wp-server-xmlrpc-url
+ org2blog/wp-server-userid
+ org2blog/wp-server-pass
+ org2blog/wp-server-blogid)
+ (metaweblog-get-recent-posts org2blog/wp-server-xmlrpc-url
+ org2blog/wp-server-blogid
+ org2blog/wp-server-userid
+ org2blog/wp-server-pass
+ 1000)))
+ post-title url title-id-map)
+ (dolist (post post-list)
+ (setq title-id-map (cons
+ (cons (cdr (assoc "title" post)) (cdr (assoc "postid" post)))
+ title-id-map)))
+ ;; Ask user to select the title
+ (setq post-title (completing-read
+ (if is-page "Select page: " "Select post: ")
+ title-id-map nil t)
+ post-id (cdr (assoc post-title title-id-map)))
+ (if post-title
+ ;; "Generate" the actual url of the post
+ (setq url (concat
+ (replace-regexp-in-string "xmlrpc\\.php$" "?p=" org2blog/wp-server-xmlrpc-url)
+ post-id))
+ ;; Insert!
+ (insert (format "[[%s][%s]]" url post-title)))))
+
(provide 'org2blog)
View
137 test-metaweblog.el
@@ -0,0 +1,137 @@
+;;; test-metaweblog.el --- tests for metaweblog.el
+;; Copyright (C) 2012 Puneeth Chaganti
+
+(require 'metaweblog)
+
+;;;;; Test env-setup ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(setq blog-xmlrpc "http://localhost/xmlrpc.php"
+ blog-user "admin"
+ blog-pass "test123"
+ blog-id "1")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Util functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun random-string (length)
+ "Return a random string of given length"
+ (let ((alpha "abcdefghijklmnopqrstuvwxyz")
+ (char-list))
+ (dotimes (char length)
+ (setq char-list (cons (string (elt "abcdefghijklmnopqrstuvwxyz" (random 25))) char-list)))
+ (mapconcat 'identity char-list "")))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(ert-deftest test-new-category ()
+ "Test if creating a new category works."
+ (let* ((category (random-string 8))
+ ;; Add new category
+ (category-id (wp-new-category blog-xmlrpc blog-user blog-pass blog-id category)))
+ ;; List all categories and check if our category is present that list
+ (setq categories (metaweblog-get-categories blog-xmlrpc blog-user blog-pass blog-id))
+ (dolist (cat categories)
+ (if (equal (cdr (assoc "categoryId" cat)) category-id)
+ (should (equal (cdr (assoc "categoryName" cat)) category))))))
+
+(ert-deftest test-new-tag ()
+ "Test if getting tags works. This test does nothing, just
+checks if Wordpress version has the API.
+FIXME: Make this a real test ..."
+ (let* ()
+ (setq tag-list (wp-get-tags blog-xmlrpc blog-user blog-pass blog-id))))
+
+(ert-deftest test-pages ()
+ "Test if creating, listing, fetching content and deleting pages works."
+ (let* ((content '(("date" . "20120817T18:30:00+0000")
+ ("title" . "Hello World")
+ ("tags" "org2blog" "emacs")
+ ("categories" "org2blog" "emacs")
+ ("post-id")
+ ("parent" . "0")
+ ("excerpt" . "")
+ ("permalink" .