Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
  • 17 commits
  • 11 files changed
  • 0 commit comments
  • 5 contributors
4 .gitignore
View
@@ -0,0 +1,4 @@
+doc/tags
+.hg
+.svn
+.hg*
41 README
View
@@ -1,41 +0,0 @@
-This is a mirror of http://www.vim.org/scripts/script.php?script_id=3510
-
-VimRepress is a plugin for managing wordpress blog from Vim, a rewritten of vimscript #1953 , which is broken for years.
-
-Now VimRepress lives with more powerful again.
-
-*REQUIREMENT*
-
-- Vim 7.3+ with python 2.6/2.7 support
-- Python Environment matched wtih Vim's support
-- python-markdown/python-markdown2 installed
-- wordpress 3.0.0 +
-
-
-
-COMMAND EXAMPLES
-
-Some commands list above contain special usage, example below may clearify them for you.
-
-
- :BlogList - List 30 recent posts.
- :BlogList page - List 30 recent pages.
- :BlogList post 100 - List 100 recent posts.
-
- :BlogNew post - Write an new post.
- :BlogNew page - Write an new page.
-
- :BlogSave - Save (defautely published.)
- :BlogSave draft - Save as draft.
-
- :BlogPreview local - Preview page/post locally in your browser.
- :BlogPreview publish - Same as `:BlogSave publish' with brower opened.
-
- :BlogOpen 679
- :BlogOpen http://your-first-blog.com/archives/679
- :BlogOpen http://your-second-blog.com/?p=679
- :BlogOpen http://your-third-blog.com/with-your-custom-permalink
-
-More detailed about this commands, type :help vimpress while you have vimrepress installed.
-
-Developing Repository: https://bitbucket.org/pentie/vimrepress
57 README.md
View
@@ -0,0 +1,57 @@
+#Welcome
+VimRepress is a plugin for managing WordPress blog from Vim, using Markdown syntax.
+
+##Features
+ * NEW/EDIT/DELETE WordPress Posts/Pages.
+ * In both Markdown / HTML format.
+ * Markdown text can be configured to be stored in the custom fields of WordPress.
+ * Upload attachments.
+ * Insert code highlight section.
+ * Preview a posts in local compiled version, or remote draft.
+ * WordPress.com account supported.
+ * Multiple account supported.
+
+##Commands Reference
+ * BlogList [post|page]
+ * BlogNew [post|page]
+ * BlogSave [publish|draft]
+ * BlogPreview [local|publish|draft]
+ * BlogUpload *[path/to/your/local/file]
+ * BlogOpen *[post id or full article URL]
+ * BlogSwitch [0,1,2 ... N, number of account in your config]
+ * BlogCode [type of lang for the \<pre\> element]
+
+ (Commands with a `*`, argument must be present.)
+
+
+##CONFIGURE
+
+Create file `~/.vimpressrc` in the following format:
+
+ [Blog0]
+ blog_url = http://a-blog.com/
+ username = admin
+ password = 123456
+
+ [Blog1]
+ blog_url = https://blog1.wordpress.com/
+ username = someone
+ password =
+ store_markdown = n
+
+ [BlogWhatEver]
+ blog_url = https://someone.wordpress.com/
+ username = someone
+ password =
+
+Hardcoding the password is optional. If a password is not provided the plugin will prompt for one the first time it's needed.
+
+`store_markdown` is also optional. If not specified then Markdown text will be stored in custom fields of WordPress. If set to `n` then the Markdown text will not be stored.
+
+###For Upgraded Users
+
+Defining account info in `.vimrc` is now obsolesced, if you have correspond defination in `.vimrc` (for older version vimpress), they will automaticly copied into `~/.vimpressrc`, now you're safe to remove the VIMPRESS defination in `.vimrc`.
+
+Users from the 2.x.x versions of vimrepress, need to run the `markdown_posts_upgrade.py` to upgrade the their posts data to be compatible with the 3.x.x version of vimrepress, or their Markdown source can not be used to re-edit by a newer vimrepress.
+
+
90 doc/vimpress.txt
View
@@ -17,27 +17,33 @@ License: Same terms as Vim itself (see |license|)
*INSTALL*
-Download vimpress_2.x.x.zip, extract it in your .vim directory:
+Download vimpress_x.x.x.zip, extract it in your .vim directory:
cd ~/.vim
- unzip /path/to/vimpress_2.x.x.zip
+ unzip /path/to/vimpress_x.x.x.zip
*CONFIGURE*
-Edit ~/.vimrc , add a variable named VIMPRESS. Multiple blog configurations are supported.
+Create file `~/.vimpressrc' in the following format:
-Example:
+ [Blog0]
+ blog_url = http://a-blog.com/
+ username = admin
+ password = 123456
-let VIMPRESS = [{'username':'user',
- \'password':'pass',
- \'blog_url':'http://your-first-blog.com/'
- \},
- \{'username':'user',
- \'blog_url':'http://your-second-blog.com/'
- \}]
+ [Blog1]
+ blog_url = https://someone.wordpress.com/
+ username = someone
+ password =
-Hardcoding the password is optional. If a password is not provided the plugin
-will prompt for one the first time it's needed.
+>
+For Upgraded Users:
+
+ Defining Account info in `.vimrc` is now obsolesced, if you have correspond defination in `.vimrc', they will automaticly copied into `~/.vimpressrc', now you're safe to remove the VIMPRESS defination in `.vimrc'.
+<
+
+Hardcoding the password is optional. If a password is not provided,
+the plugin will prompt for one the first time it's needed.
If you need Markdown support, simply run `sudo apt-get install python-markdown' in Ubuntu.
@@ -125,61 +131,3 @@ Some commands list above contain special usage, example below may clearify them
:BlogOpen http://your-third-blog.com/with-your-custom-permalink
<
-*CHANGE_LOG*
-
- 2011 May. 15 [by Preston]
- Upgrade to 2.0 beta
- Different command structure
- Markdown file uploaded when post is saved and interpreted
- when opened.
- The open command takes many types of parameters
-
- [by Conner]
- Add: autocompletion for tags and categories with ^X^U
- Add: prompts for password when not hardcoded
- Add: BlogSwitch now accepts an index parameter
- Add: Delete function in BlogList view.
- *Add: HTML highlighting for blog syntax
- *Add: Delete command
- *(not yet included in Preston's release version)
-
- 2011 Mar. 24 [by Lenin Lee]
- Fix: use setl instead of set to set option value;
- Add: Detect current buffer content before switch to vimpress
- views, open a split buffer to avoid conflicts.
- Add: Commands to manage wordpress pages.
-
- [by Preston]
- Add: Auto charset convert for non-utf8 environment.(Win)
- Add: Use python markdown module. Both markdown and markdown2
- are supported.
-
- 2011 Mar. 15 [by Preston]
- Fix: MarkdownNewPost may override original mkd source file.
- Add: MarkdownNewPost command detects title begins with
- "#" in first 10 lines of markdown source, copy the line
- striped "#" to the new post view.
-
- 2011 Mar. 7 [by Preston]
- Add: MarkdownPreview command to preiview markdown in browser.
- Add: MarkdownNewPost command to convert a markdown
- written post into html and set to the new post view.
-
-
- 2011 Mar. 4 [by Preston]
- Add: Move blog config info to personal .vimrc
- Add: Multiple blog config is now supported with :BlogSwitch
- command.
- Add: Show which blog your editing at :BlogList view.
- Fix: bug running :BlogList in the List view got error.
-
- 2011 Feb. 15 [by Preston]
- Add: BlogPreview Command.
- Add: BlogCode command args to specify code type
- Change: blog_url uses pure address.
- Code: Some code pretty work.
-
- 2010 Aug. 20 [by Justin]
- Fixed a bug with BlogSave command, and added feature to take
- an existing document and use the BlogNew command to convert
- it to a blog post (which can be saved with the header intact).
273 markdown_posts_upgrade.py
View
@@ -0,0 +1,273 @@
+#!/usr/bin/env python2
+import urllib2, xmlrpclib, re, os, sys
+import getpass
+
+class VimPressException(Exception):
+ pass
+
+class VimPressFailedGetMkd(VimPressException):
+ pass
+
+
+class DataObject(object):
+
+ #CONST
+ DEFAULT_LIST_COUNT = "15"
+ IMAGE_TEMPLATE = '<a href="%(url)s"><img title="%(file)s" alt="%(file)s" src="%(url)s" class="aligncenter" /></a>'
+ MARKER = dict(bg = "=========== Meta ============",
+ mid = "=============================",
+ ed = "========== Content ==========",
+ more = '"====== Press Here for More ======',
+ list_title = '"====== %(edit_type)s List in %(blog_url)s =========')
+ LIST_VIEW_KEY_MAP = dict(enter = "<enter>", delete = "<delete>")
+ DEFAULT_META = dict(strid = "", title = "", slug = "",
+ cats = "", tags = "", editformat = "Markdown",
+ edittype = "post", textattach = '')
+ TAG_STRING = "<!-- #VIMPRESS_TAG# %(url)s %(file)s -->"
+ TAG_RE = re.compile(TAG_STRING % dict(url = '(?P<mkd_url>\S+)', file = '(?P<mkd_name>\S+)'))
+ CUSTOM_FIELD_KEY = "mkd_text"
+
+ #Temp variables.
+ __xmlrpc = None
+ __conf_index = 0
+ __config = None
+
+ view = 'edit'
+ vimpress_temp_dir = ''
+
+ blog_username = property(lambda self: self.xmlrpc.username)
+ blog_url = property(lambda self: self.xmlrpc.blog_url)
+ conf_index = property(lambda self:self.__conf_index)
+
+ xmlrpc = None
+
+
+class wp_xmlrpc(object):
+
+ def __init__(self, blog_url, username, password):
+ self.blog_url = blog_url
+ self.username = username
+ self.password = password
+ p = xmlrpclib.ServerProxy(os.path.join(blog_url, "xmlrpc.php"))
+ self.mw_api = p.metaWeblog
+ self.wp_api = p.wp
+ self.mt_api = p.mt
+ self.demo_api = p.demo
+
+ assert self.demo_api.sayHello() == "Hello!", "XMLRPC Error with communication with '%s'@'%s'" % \
+ (username, blog_url)
+
+ self.cache_reset()
+
+ def cache_reset(self):
+ self.__cache_post_titles = []
+ self.__post_title_max = False
+
+ def cache_remove_post(self, postid):
+ for p in self.__cache_post_titles:
+ if p["postid"] == str(postid):
+ self.__cache_post_titles.remove(p)
+ break
+
+ is_reached_title_max = property(lambda self: self.__post_title_max)
+
+ new_post = lambda self, post_struct, is_publish: self.mw_api.newPost('',
+ self.username, self.password, post_struct, is_publish)
+
+ get_post = lambda self, post_id: self.mw_api.getPost(post_id,
+ self.username, self.password)
+
+ edit_post = lambda self, post_id, post_struct: self.mw_api.editPost(post_id,
+ self.username, self.password, post_struct )
+
+ delete_post = lambda self, post_id: self.mw_api.deletePost('', post_id, self.username,
+ self.password, '')
+
+ def get_recent_post_titles(self, retrive_count = 0):
+ if retrive_count > len(self.__cache_post_titles) and not self.is_reached_title_max:
+ self.__cache_post_titles = self.mt_api.getRecentPostTitles('',
+ self.username, self.password, retrive_count)
+ if len(self.__cache_post_titles) < retrive_count:
+ self.__post_title_max = True
+
+ return self.__cache_post_titles
+
+ get_categories = lambda self:self.mw_api.getCategories('', self.username, self.password)
+
+ new_media_object = lambda self, object_struct: self.mw_api.newMediaObject('', self.username,
+ self.password, object_struct)
+
+ get_page = lambda self, page_id: self.wp_api.getPage('', page_id, self.username, self.password)
+
+ delete_page = lambda self, page_id: self.wp_api.deletePage('',
+ self.username, self.password, page_id)
+
+ get_page_list = lambda self: self.wp_api.getPageList('', self.username, self.password)
+
+def blog_get_mkd_attachment(post):
+ """
+ Attempts to find a vimpress tag containing a URL for a markdown attachment and parses it.
+ @params post - the content of a post
+ @returns a dictionary with the attachment's content and URL
+ """
+ attach = dict()
+ try:
+ lead = post.rindex("<!-- ")
+ data = re.search(g_data.TAG_RE, post[lead:])
+ if data is None:
+ raise VimPressFailedGetMkd("Attached markdown not found.")
+ attach.update(data.groupdict())
+ attach["mkd_rawtext"] = urllib2.urlopen(attach["mkd_url"]).read()
+ except (IOError, ValueError):
+ raise VimPressFailedGetMkd("The attachment URL was found but was unable to be read.")
+
+ return attach
+
+def blog_update(post, content, attach):
+ lead = content.rindex("<!-- ")
+ new_content = content[:lead]
+ markdown_text = attach["mkd_rawtext"]
+ post_struct = post
+
+ try:
+ strid = post["postid"]
+ except KeyError:
+ strid = post["page_id"]
+
+ if len(new_content.strip()) == 0:
+ new_content = 'Empty Post'
+
+ post_struct["description"] = new_content
+
+ if len(markdown_text) > 0:
+ for f in post_struct["custom_fields"]:
+ if f["key"] == g_data.CUSTOM_FIELD_KEY:
+ f["value"] = markdown_text
+ break
+ else:
+ post_struct["custom_fields"].append(dict(key = g_data.CUSTOM_FIELD_KEY, value = markdown_text))
+
+ try:
+ g_data.xmlrpc.edit_post(strid, post_struct )
+ except xmlrpclib.Fault, e:
+ raise
+
+
+def post_struct_get_content(data):
+ content = data["description"]
+ post_more = data.get("mt_text_more", '')
+ page_more = data.get("text_more", '')
+
+ if len(post_more) > 0:
+ content += '<!--more-->' + post_more
+ elif len(page_more) > 0:
+ content += '<!--more-->' + page_more
+
+ return content
+
+def loop_proccess_posts(posts, edit_type):
+ print "ID Title"
+ for post in posts:
+ if edit_type == "page":
+ print u"%(page_id)s\t%(page_title)s" % post, '... ',
+ sys.stdout.flush()
+ page_id = post["page_id"].encode("utf-8")
+ data = g_data.xmlrpc.get_page(page_id)
+ elif edit_type == "post":
+ print u"%(postid)s\t%(title)s ... " % post,
+ sys.stdout.flush()
+ post_id = post["postid"].encode("utf-8")
+ data = g_data.xmlrpc.get_post(post_id)
+
+ content = post_struct_get_content(data)
+ try:
+ attach = blog_get_mkd_attachment(content)
+ except VimPressFailedGetMkd:
+ print "No Markdown Attached."
+ else:
+ blog_update(data, content, attach)
+ attachements_proccessed.append(attach["mkd_name"])
+ print "Updated."
+
+ print "\n\n"
+
+print """
+
+Vimrepress 3.x upgrade script
+
+WHY:
+
+ The older (2.x) vimrepress stores your originally
+ written markdown text in an attachment with the
+ post on wordpress.
+
+ A better way is implemented in the new (3.x)
+ vimrepress, the markdown texts stores in the custom
+ field of a post.
+
+ If you used vimrepress 2.x to write your blog before,
+ and want the 3.x to be able to edit your old posts,
+ this script is needed to convert the attachments
+ content into the custom field.
+
+HOW:
+
+ Fillin the address/username/password as asked, script
+ will scan through your posts, when a markdown attached
+ post found, it will download and read it into the
+ database.
+
+I tested this script works flawlessly to my own blog,
+but I don't guarantee no exceptions in other circumstance,
+I don't respons for any data lost.
+
+Backup your wordpress with this plugin: WP-DB-Backup
+ ( http://wordpress.org/extend/plugins/wp-db-backup/ )
+
+or phpmyadmin/adminer/mysqldump anything you like it most.
+
+ WARNNING
+#########################################################
+Warnning: Backup your wordpress database before procceed.
+Warnning: Backup your wordpress database before procceed.
+Warnning: Backup your wordpress database before procceed.
+#########################################################
+"""
+
+URL = raw_input("Blog URL: ")
+USER = raw_input("USERNAME: ")
+PASS = getpass.getpass()
+
+i = raw_input("Have you backed up your wordpress database? [y/N]")
+if i.lower() == 'n' or i == '':
+ print "----> Go and do that, don't risk your data."
+ sys.exit(1)
+
+g_data = DataObject()
+g_data.xmlrpc = wp_xmlrpc(URL, USER, PASS)
+
+attachements_proccessed = []
+
+i = raw_input("Upgrade pages ?[Y/n]")
+if i.lower() == 'y' or i == '':
+ print "Upgrade pages ..."
+ pages = g_data.xmlrpc.get_page_list()
+ loop_proccess_posts(pages, "page")
+
+i = raw_input("Upgrade Posts ?[Y/n]")
+if i.lower() == 'y' or i == '':
+ count = raw_input("How many recent posts to process? [100]")
+ if count == '':
+ count = '100'
+ assert isinstance(int(count), int), "input a integer please."
+ posts = g_data.xmlrpc.get_recent_post_titles(count)
+ loop_proccess_posts(posts, "post")
+
+if len(attachements_proccessed) > 0:
+ print "All Done. Congras."
+ print "You may now delete this attachments from wordpress panel."
+ print URL+"/wp-admin/upload.php"
+ print
+ print "\n".join(attachements_proccessed)
+
+
830 plugin/blog.vim
View
@@ -1,830 +0,0 @@
-"#######################################################################
-" Copyright (C) 2007 Adrien Friggeri.
-"
-" 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 2, or (at your option)
-" any later version.
-"
-" This program is distributed in the hope that it will be useful,
-" but WITHOUT ANY WARRANTY; without even the implied warranty of
-" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-" GNU General Public License for more details.
-"
-" You should have received a copy of the GNU General Public License
-" along with this program; if not, write to the Free Software Foundation,
-" Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-"
-" Maintainer: Adrien Friggeri <adrien@friggeri.net>
-" Pigeond <http://pigeond.net/blog/>
-" Justin Sattery <justin.slattery@fzysqr.com>
-" Lenin Lee <lenin.lee@gmail.com>
-" Conner McDaniel <connermcd@gmail.com>
-"
-" Forked: Preston M.[BOYPT] <pentie@gmail.com>
-"
-" URL: http://www.friggeri.net/projets/vimblog/
-" http://pigeond.net/blog/2009/05/07/vimpress-again/
-" http://pigeond.net/git/?p=vimpress.git
-" http://apt-blog.net
-" http://fzysqr.com/
-"
-" VimRepress
-" - A mod of a mod of a mod of Vimpress.
-" - A vim plugin fot writting your wordpress blog.
-"
-" Version: 2.1.5
-"
-" Configure: Add blog configure into your .vimrc (password optional)
-"
-" let VIMPRESS=[{'username':'user',
-" \'password':'pass',
-" \'blog_url':'http://your-first-blog.com/'
-" \},
-" \{'username':'user',
-" \'blog_url':'http://your-second-blog.com/'
-" \}]
-"
-"#######################################################################
-
-if !has("python")
- finish
-endif
-
-function! CompSave(ArgLead, CmdLine, CursorPos)
- return "publish\ndraft\n"
-endfunction
-
-function! CompPrev(ArgLead, CmdLine, CursorPos)
- return "local\npublish\ndraft\n"
-endfunction
-
-function! CompEditType(ArgLead, CmdLine, CursorPos)
- return "post\npage\n"
-endfunction
-
-fun! Completable(findstart, base)
- if a:findstart
- " locate the start of the word
- let line = getline('.')
- let start = col('.') - 1
- while start > 0 && line[start - 1] =~ '\a'
- let start -= 1
- endwhile
- return start
- else
- " find matching items
- let res = []
- for m in split(s:completable,"|")
- if m =~ '^' . a:base
- call add(res, m)
- endif
- endfor
- return res
- endif
-endfun
-
-command! -nargs=* -complete=custom,CompEditType BlogList exec('py blog_list(<f-args>)')
-command! -nargs=? -complete=custom,CompEditType BlogNew exec('py blog_new(<f-args>)')
-command! -nargs=? -complete=custom,CompSave BlogSave exec('py blog_save(<f-args>)')
-command! -nargs=? -complete=custom,CompPrev BlogPreview exec('py blog_preview(<f-args>)')
-command! -nargs=1 -complete=file BlogUpload exec('py blog_upload_media(<f-args>)')
-command! -nargs=1 BlogOpen exec('py blog_guess_open(<f-args>)')
-command! -nargs=? BlogSwitch exec('py blog_config_switch(<f-args>)')
-command! -nargs=? BlogCode exec('py blog_append_code(<f-args>)')
-
-python << EOF
-# -*- coding: utf-8 -*-
-import urllib, urllib2, vim, xml.dom.minidom, xmlrpclib, sys, string, re, os, mimetypes, webbrowser, tempfile, time
-try:
- import markdown
-except ImportError:
- try:
- import markdown2 as markdown
- except ImportError:
- class markdown_stub(object):
- def markdown(self, n):
- raise VimPressException("The package python-markdown is required and is either not present or not properly installed.")
- markdown = markdown_stub()
-
-image_template = '<a href="%(url)s"><img title="%(file)s" alt="%(file)s" src="%(url)s" class="aligncenter" /></a>'
-blog_username = None
-blog_password = None
-blog_url = None
-blog_conf_index = 0
-vimpress_view = 'edit'
-vimpress_temp_dir = ''
-
-mw_api = None
-wp_api = None
-marker = ("=========== Meta ============", "=============================", "========== Content ==========")
-list_view_key_map = dict(enter = "<enter>", delete = "<delete>")
-
-tag_string = "<!-- #VIMPRESS_TAG# %(url)s %(file)s -->"
-tag_re = re.compile(tag_string % dict(url = '(?P<mkd_url>\S+)', file = '(?P<mkd_name>\S+)'))
-
-default_meta = dict(strid = "", title = "", slug = "",
- cats = "", tags = "", editformat = "Markdown", edittype = "post", textattach = '')
-
-class VimPressException(Exception):
- pass
-
-class VimPressFailedGetMkd(VimPressException):
- pass
-
-def blog_meta_parse():
- """
- Parses the meta data region of a blog editing buffer.
- @returns a dictionary of the meta data
- """
- meta = dict()
- start = 0
- while not vim.current.buffer[start][1:].startswith(marker[0]):
- start +=1
-
- end = start + 1
- while not vim.current.buffer[end][1:].startswith(marker[2]):
- if not vim.current.buffer[end].startswith('"===='):
- line = vim.current.buffer[end][1:].strip().split(":")
- k, v = line[0].strip().lower(), ':'.join(line[1:])
- meta[k.strip().lower()] = v.strip()
- end += 1
-
- meta["post_begin"] = end + 1
- return meta
-
-def blog_meta_area_update(**kw):
- """
- Updates the meta data region of a blog editing buffer.
- @params **kwargs - keyworded arguments
- """
- start = 0
- while not vim.current.buffer[start][1:].startswith(marker[0]):
- start +=1
-
- end = start + 1
- while not vim.current.buffer[end][1:].startswith(marker[2]):
- if not vim.current.buffer[end].startswith('"===='):
- line = vim.current.buffer[end][1:].strip().split(":")
- k, v = line[0].strip().lower(), ':'.join(line[1:])
- if k in kw:
- new_line = "\"%s: %s" % (line[0], kw[k])
- vim.current.buffer[end] = new_line
- end += 1
-
-def blog_fill_meta_area(meta):
- """
- Creates the meta data region for a blog editing buffer using a dictionary of meta data. Empty keywords
- are replaced by default values from the default_meta variable.
- @params meta - a dictionary of meta data
- """
- for k in default_meta.keys():
- if k not in meta:
- meta[k] = default_meta[k]
-
- meta.update(dict(bg = marker[0], mid = marker[1], ed = marker[2]))
- template = dict( \
- post = \
-""""%(bg)s
-"StrID : %(strid)s
-"Title : %(title)s
-"Slug : %(slug)s
-"Cats : %(cats)s
-"Tags : %(tags)s
-"%(mid)s
-"EditType : %(edittype)s
-"EditFormat : %(editformat)s
-"TextAttach : %(textattach)s
-"%(ed)s""",
- page = \
-""""%(bg)s
-"StrID : %(strid)s
-"Title : %(title)s
-"Slug : %(slug)s
-"%(mid)s
-"EditType : %(edittype)s
-"EditFormat : %(editformat)s
-"TextAttach : %(textattach)s
-"%(ed)s""")
-
- if meta["edittype"] not in ("post", "page"):
- raise VimPressException("Invalid option: %(edittype)s " % meta)
- meta_text = template[meta["edittype"].lower()] % meta
- meta = meta_text.split('\n')
- vim.current.buffer[0] = meta[0]
- vim.current.buffer.append(meta[1:])
-
-def blog_get_mkd_attachment(post):
- """
- Attempts to find a vimpress tag containing a URL for a markdown attachment and parses it.
- @params post - the content of a post
- @returns a dictionary with the attachment's content and URL
- """
-
- attach = dict()
- try:
- lead = post.rindex("<!-- ")
- data = re.search(tag_re, post[lead:])
- if data is None:
- raise ValueError()
- attach.update(data.groupdict())
- attach["mkd_rawtext"] = urllib2.urlopen(attach["mkd_url"]).read()
- except ValueError, e:
- return dict()
- except IOError:
- raise VimPressFailedGetMkd("The attachment URL was found but was unable to be read.")
-
- return attach
-
-def blog_upload_markdown_attachment(post_id, attach_name, mkd_rawtext):
- """
- Uploads the markdown attachment.
- @params post_id - the id of the post or page
- attach_name - the name of the attachment
- mkd_rawtext - the Markdown content
- """
- bits = xmlrpclib.Binary(mkd_rawtext)
-
- # New Post, new file
- if post_id == '' or attach_name == '':
- attach_name = "vimpress_%s_mkd.txt" % hex(int(time.time()))[2:]
- overwrite = False
- else:
- overwrite = True
-
- sys.stdout.write("Markdown file uploading ... ")
- result = mw_api.newMediaObject(1, blog_username, blog_password,
- dict(name = attach_name,
- type = "text/plain", bits = bits,
- overwrite = overwrite))
- sys.stdout.write("%s\n" % result["file"])
- return result
-
-def __exception_check(func):
- def __check(*args, **kwargs):
- try:
- return func(*args, **kwargs)
- except VimPressException, e:
- sys.stderr.write(str(e))
- except xmlrpclib.Fault, e:
- sys.stderr.write("xmlrpc error: %s" % e.faultString.encode("utf-8"))
- except xmlrpclib.ProtocolError, e:
- sys.stderr.write("xmlrpc error: %s %s" % (e.url, e.errmsg))
- except IOError, e:
- sys.stderr.write("network error: %s" % e)
-
- return __check
-
-def __vim_encoding_check(func):
- def __check(*args, **kw):
- orig_enc = vim.eval("&encoding")
- if orig_enc != "utf-8":
- modified = vim.eval("&modified")
- buf_list = '\n'.join(vim.current.buffer).decode(orig_enc).encode('utf-8').split('\n')
- del vim.current.buffer[:]
- vim.command("setl encoding=utf-8")
- vim.current.buffer[0] = buf_list[0]
- if len(buf_list) > 1:
- vim.current.buffer.append(buf_list[1:])
- if modified == '0':
- vim.command('setl nomodified')
- return func(*args, **kw)
- return __check
-
-def __xmlrpc_api_check(func):
- def __check(*args, **kw):
- if wp_api is None or mw_api is None:
- blog_update_config()
- return func(*args, **kw)
- return __check
-
-@__exception_check
-@__vim_encoding_check
-@__xmlrpc_api_check
-def blog_save(pub = "draft"):
- """
- Saves the current editing buffer.
- @params pub - either "draft" or "publish"
- """
- if vimpress_view != 'edit':
- raise VimPressException("Command not available at list view.")
- if pub not in ("publish", "draft"):
- raise VimPressException(":BlogSave draft|publish")
-
- is_publish = (pub == "publish")
-
- meta = blog_meta_parse()
- rawtext = '\n'.join(vim.current.buffer[meta["post_begin"]:])
-
- #Translate markdown and upload as attachment
- if meta["editformat"].strip().lower() == "markdown":
- attach = blog_upload_markdown_attachment(
- meta["strid"], meta["textattach"], rawtext)
- blog_meta_area_update(textattach = attach["file"])
- text = markdown.markdown(rawtext.decode('utf-8')).encode('utf-8')
-
- # Add tag string at the last of the post.
- text += tag_string % attach
- else:
- text = rawtext
-
- edit_type = meta["edittype"]
- strid = meta["strid"]
-
- if edit_type.lower() not in ("post", "page"):
- raise VimPressException(
- "Fail to work with edit type %s " % edit_type)
-
- post_struct = dict(title = meta["title"], wp_slug = meta["slug"],
- description = text)
- if edit_type == "post":
- post_struct.update(categories = meta["cats"].split(','),
- mt_keywords = meta["tags"].split(','))
-
- # New posts
- if strid == '':
- if edit_type == "post":
- strid = mw_api.newPost('', blog_username, blog_password,
- post_struct, is_publish)
- else:
- strid = wp_api.newPage('', blog_username, blog_password,
- post_struct, is_publish)
-
- meta["strid"] = strid
-
- # update meat area if slug or categories is empty
- if edit_type == "post":
- if meta["slug"] == '' or meta["cats"] == '':
- data = mw_api.getPost(strid, blog_username, blog_password)
- cats = ",".join(data["categories"]).encode("utf-8")
- slug = data["wp_slug"].encode("utf-8")
- meta["cats"] = cats
- meta["slug"] = slug
- else:
- if meta["slug"] == '':
- data = wp_api.getPage('', strid, blog_username, blog_password)
- slug = data["wp_slug"].encode("utf-8")
- meta["slug"] = slug
-
- blog_meta_area_update(**meta)
-
- notify = "%s %s. ID=%s" % \
- (edit_type.capitalize(),
- "Published" if is_publish else "Saved as draft", strid)
-
- # Old posts
- else:
- if edit_type == "post":
- mw_api.editPost(strid, blog_username, blog_password,
- post_struct, is_publish)
- elif edit_type == "page":
- wp_api.editPage('', strid, blog_username, blog_password,
- post_struct, is_publish)
-
- notify = "%s edited and %s. ID=%s" % \
- (edit_type.capitalize(), "published" if is_publish else "saved as a draft", strid)
-
- sys.stdout.write(notify)
- vim.command('setl nomodified')
-
- return meta
-
-@__exception_check
-@__vim_encoding_check
-@__xmlrpc_api_check
-def blog_new(edit_type = "post"):
- """
- Creates a new editing buffer of specified type.
- @params edit_type - either "post" or "page"
- """
- global vimpress_view
-
- if edit_type.lower() not in ("post", "page"):
- raise VimPressException("Invalid option: %s " % edit_type)
-
- if vimpress_view.startswith("list"):
- currentContent = ['']
- for v in list_view_key_map.values():
- if vim.eval("mapcheck('%s')" % v):
- vim.command('unmap <buffer> %s' % v)
- else:
- currentContent = vim.current.buffer[:]
-
- blog_wise_open_view()
- vimpress_view = 'edit'
- meta_dict = dict(edittype = edit_type)
- blog_fill_meta_area(meta_dict)
- vim.current.buffer.append(currentContent)
- vim.current.window.cursor = (1, 0)
- vim.command('setl nomodified')
- vim.command('setl textwidth=0')
-
-@__xmlrpc_api_check
-def blog_edit(edit_type, post_id):
- """
- Opens a new editing buffer with blog content of specified type and id.
- @params edit_type - either "post" or "page"
- post_id - the id of the post or page
- """
- global vimpress_view
- vimpress_view = 'edit'
-
- blog_wise_open_view()
-
- if edit_type.lower() not in ("post", "page"):
- raise VimPressException("Invalid option: %s " % edit_type)
-
- if edit_type.lower() == "post":
- data = mw_api.getPost(post_id, blog_username, blog_password)
- else:
- data = wp_api.getPage('', post_id, blog_username, blog_password)
-
- meta_dict = dict(\
- strid = str(post_id),
- title = data["title"].encode("utf-8"),
- slug = data["wp_slug"].encode("utf-8"))
- content = data["description"]
- post_more = data.get("mt_text_more", '')
- page_more = data.get("text_more", '')
-
- if len(post_more) > 0:
- content += '<!--more-->' + post_more
- elif len(page_more) > 0:
- content += '<!--more-->' + page_more
-
- content = content.encode("utf-8")
-
- if edit_type.lower() == "post":
- meta_dict["cats"] = ",".join(data["categories"]).encode("utf-8")
- meta_dict["tags"] = data["mt_keywords"].encode("utf-8")
-
- meta_dict['editformat'] = "HTML"
- meta_dict['edittype'] = edit_type
-
- try:
- attach = blog_get_mkd_attachment(content)
- if "mkd_rawtext" in attach:
- meta_dict['editformat'] = "Markdown"
- meta_dict['textattach'] = attach["mkd_name"]
- content = attach["mkd_rawtext"]
- except VimPressFailedGetMkd:
- pass
-
- blog_fill_meta_area(meta_dict)
- meta = blog_meta_parse()
- vim.current.buffer.append(content.split('\n'))
- vim.current.window.cursor = (meta["post_begin"], 0)
- vim.command('setl nomodified')
- vim.command('setl textwidth=0')
-
- for v in list_view_key_map.values():
- if vim.eval("mapcheck('%s')" % v):
- vim.command('unmap <buffer> %s' % v)
-
-@__xmlrpc_api_check
-def blog_delete(edit_type, post_id):
- """
- Deletes a page or post of specified id.
- @params edit_type - either "page" or "post"
- post_id - the id of the post or page
- """
- global vimpress_view
- if edit_type.lower() not in ("post", "page"):
- raise VimPressException("Invalid option: %s " % edit_type)
-
- if edit_type.lower() == "post":
- deleted = mw_api.deletePost('0123456789ABCDEF', post_id, blog_username, blog_password, True)
- else:
- deleted = wp_api.deletePage('', blog_username, blog_password, post_id)
-
- if deleted:
- sys.stdout.write("Deleted %s id %s. \n" % (edit_type, str(post_id)))
- else:
- sys.stdout.write("There was a problem deleting the %s.\n" % edit_type)
-
- if vimpress_view.startswith("list"):
- blog_list(edit_type)
-
-@__exception_check
-def blog_list_on_key_press(action):
- """
- Calls blog open on the current line of a listing buffer.
- """
- global vimpress_view
- if action.lower() not in ("open", "delete"):
- raise VimPressException("Invalid option: %s" % action)
-
- global vimpress_view
- row = vim.current.window.cursor[0]
- line = vim.current.buffer[row - 1]
- id = line.split()[0]
- title = line[len(id):].strip()
-
- try:
- int(id)
- except ValueError:
- raise VimPressException("Move cursor to a post/page line and press KEY.")
-
- if len(title) > 30:
- title = title[:30] + ' ...'
-
- if action.lower() == "delete":
- confirm = vim_input("Confirm Delete [%s]: %s? [yes/NO]" % (id,title))
- if confirm != 'yes':
- sys.stdout.write("Delete Aborted.\n")
- return
-
- vim.command("setl modifiable")
- del vim.current.buffer[:]
- vim.command("setl nomodified")
-
- if vimpress_view == "list_page":
- edit_type = "page"
- elif vimpress_view == "list_post":
- edit_type = "post"
- else:
- raise VimPressException("Command only available in list view.")
-
- if action == "open":
- blog_edit(edit_type, int(id))
- elif action == "delete":
- blog_delete(edit_type, int(id))
-
-@__exception_check
-@__vim_encoding_check
-@__xmlrpc_api_check
-def blog_list(edit_type = "post", count = "30"):
- """
- Creates a listing buffer of specified type.
- @params edit_type - either "post(s)" or "page(s)"
- count - number to show (only for posts)
- """
- global vimpress_view
- vimpress_view = 'list'
-
- blog_wise_open_view()
- vim.current.buffer[0] = "\"====== List of %ss in %s =========" % (edit_type.capitalize(), blog_url)
-
- if edit_type.lower() not in ("post", "posts", "page", "pages"):
- raise VimPressException("Invalid option: %s " % edit_type)
-
- if edit_type.lower() in ("post", "posts"):
- vimpress_view = 'list_post'
- allposts = mw_api.getRecentPosts('',blog_username, blog_password, int(count))
- vim.current.buffer.append(\
- [(u"%(postid)s\t%(title)s" % p).encode('utf8') for p in allposts])
- else:
- vimpress_view = 'list_page'
- pages = wp_api.getPageList('', blog_username, blog_password)
- vim.current.buffer.append(\
- [(u"%(page_id)s\t%(page_title)s" % p).encode('utf8') for p in pages])
-
- vim.command("setl nomodified")
- vim.command("setl nomodifiable")
- vim.current.window.cursor = (2, 0)
- vim.command("map <silent> <buffer> %(enter)s :py blog_list_on_key_press('open')<cr>" % list_view_key_map)
- vim.command("map <silent> <buffer> %(delete)s :py blog_list_on_key_press('delete')<cr>" % list_view_key_map)
- sys.stdout.write("Press <Enter> to edit. <Delete> to move to trash.\n")
-
-@__exception_check
-@__vim_encoding_check
-@__xmlrpc_api_check
-def blog_upload_media(file_path):
- """
- Uploads a file to the blog.
- @params file_path - the file's path
- """
- if vimpress_view != 'edit':
- raise VimPressException("Command not available at list view.")
- if not os.path.exists(file_path):
- raise VimPressException("File does not exist: %s" % file_path)
-
- name = os.path.basename(file_path)
- filetype = mimetypes.guess_type(file_path)[0]
- with open(file_path) as f:
- bits = xmlrpclib.Binary(f.read())
-
- result = mw_api.newMediaObject('', blog_username, blog_password,
- dict(name = name, type = filetype, bits = bits))
-
- ran = vim.current.range
- if filetype.startswith("image"):
- img = image_template % result
- ran.append(img)
- else:
- ran.append(result["url"])
- ran.append('')
-
-@__exception_check
-@__vim_encoding_check
-def blog_append_code(code_type = ""):
- if vimpress_view != 'edit':
- raise VimPressException("Command not available at list view.")
- html = \
-"""<pre %s>
-</pre>"""
- if code_type != "":
- args = 'lang="%s" line="1"' % code_type
- else:
- args = 'lang="text"'
-
- row, col = vim.current.window.cursor
- code_block = (html % args).split('\n')
- vim.current.range.append(code_block)
- vim.current.window.cursor = (row + len(code_block), 0)
-
-@__exception_check
-@__vim_encoding_check
-def blog_preview(pub = "local"):
- """
- Opens a browser window displaying the content.
- @params pub - If "local", the content is shown in a browser locally.
- If "draft", the content is saved as a draft and previewed remotely.
- If "publish", the content is published and displayed remotely.
- """
- if vimpress_view != 'edit':
- raise VimPressException("Command not available at list view.")
- meta = blog_meta_parse()
- rawtext = '\n'.join(vim.current.buffer[meta["post_begin"]:])
-
- if pub == "local":
- if meta["editformat"].strip().lower() == "markdown":
- html = markdown.markdown(rawtext.decode('utf-8')).encode('utf-8')
- html_preview(html, meta)
- else:
- html_preview(rawtext, meta)
- elif pub == "publish" or pub == "draft":
- meta = blog_save(pub)
- if meta["edittype"] == "page":
- prev_url = "%s?pageid=%s&preview=true" % (blog_url, meta["strid"])
- else:
- prev_url = "%s?p=%s&preview=true" % (blog_url, meta["strid"])
- webbrowser.open(prev_url)
- if pub == "draft":
- sys.stdout.write("\nYou have to login in the browser to preview the post when save as draft.")
- else:
- raise VimPressException("Invalid option: %s " % pub)
-
-
-@__exception_check
-def blog_guess_open(what):
- """
- Tries several methods to get the post id from different user inputs, such as args, url, postid etc.
- """
- post_id = ''
- blog_index = -1
- if type(what) is str:
-
- for i, p in enumerate(vim.eval("VIMPRESS")):
- if what.startswith(p["blog_url"]):
- blog_index = i
-
- # User input a url contained in the profiles
- if blog_index != -1:
- guess_id = re.search(r"\S+?p=(\d+)$", what)
-
- # permantlinks
- if guess_id is None:
-
- # try again for /archives/%post_id%
- guess_id = re.search(r"\S+/archives/(\d+)", what)
-
- # fail, try get full link from headers
- if guess_id is None:
- headers = urllib.urlopen(what).headers.headers
- for link in headers:
- if link.startswith("Link:"):
- post_id = re.search(r"<\S+?p=(\d+)>", link).group(1)
-
- # fail, just give up
- if post_id == '':
- raise VimPressException("Failed to get post/page id from '%s'." % what)
- else:
- post_id = guess_id.group(1)
-
- # full link with ID (http://blog.url/?p=ID)
- else:
- post_id = guess_id.group(1)
-
- # Uesr input something not a usabe url, try numberic
- else:
- try:
- post_id = str(int(what))
- except ValueError:
- pass
-
- # detected something
- if post_id != '':
- if blog_index != -1 and blog_index != blog_conf_index:
- blog_config_switch(blog_index)
- blog_edit("post", post_id)
- else:
- raise VimPressException("Failed to get post/page id from '%s'." % what)
-
-
-@__exception_check
-def blog_update_config():
- """
- Updates the script's configuration variables.
- """
- global blog_username, blog_password, blog_url, mw_api, wp_api
- try:
- config = vim.eval("VIMPRESS")[blog_conf_index]
- blog_username = config['username']
- blog_url = config['blog_url']
- sys.stdout.write("Connecting to %s \n" % blog_url)
- blog_password = config.get('password', '')
- if blog_password == '':
- blog_password = vim_input("Enter password for %s" % blog_url, True)
- mw_api = xmlrpclib.ServerProxy("%s/xmlrpc.php" % blog_url).metaWeblog
- wp_api = xmlrpclib.ServerProxy("%s/xmlrpc.php" % blog_url).wp
-
- # Setting tags and categories for completefunc
- terms = []
- terms.extend([i["description"].encode("utf-8")
- for i in mw_api.getCategories('', blog_username, blog_password)])
- # adding tags may make the menu too much items to choose.
- #terms.extend([i["name"].encode("utf-8") for i in wp_api.getTags('', blog_username, blog_password)])
- vim.command('let s:completable = "%s"' % '|'.join(terms))
-
- except vim.error:
- raise VimPressException("Could not find vimpress configuration. Please read ':help vimpress' for more information.")
- except KeyError, e:
- raise VimPressException("Configuration error: %s" % e)
-
-@__vim_encoding_check
-def vim_input(message = 'input', secret = False):
- vim.command('call inputsave()')
- vim.command("let user_input = %s('%s :')" % (("inputsecret" if secret else "input"), message))
- vim.command('call inputrestore()')
- return vim.eval('user_input')
-
-@__exception_check
-@__vim_encoding_check
-def blog_config_switch(conf_index = -1):
- """
- Switches the blog to the next index of the configuration array.
- """
- global blog_conf_index
- try:
- conf_index = int(conf_index)
- except ValueError:
- raise VimPressException("Invalid Index: %s" % conf_index)
-
- conf = vim.eval("VIMPRESS")
- if conf_index == -1:
- blog_conf_index += 1
- if blog_conf_index >= len(conf):
- blog_conf_index = 0
- else:
- if conf_index >= len(conf):
- raise VimPressException("Invalid Index: %d" % conf_index)
- blog_conf_index = conf_index
-
- blog_update_config()
- if vimpress_view.startswith('list'):
- blog_list()
- sys.stdout.write("Vimpress switched to %s" % blog_url)
-
-def html_preview(text_html, meta):
- """
- Opens a browser with a local preview of the content.
- @params text_html - the html content
- meta - a dictionary of the meta data
- """
- global vimpress_temp_dir
- if vimpress_temp_dir == '':
- vimpress_temp_dir = tempfile.mkdtemp(suffix="vimpress")
-
- html = \
-"""<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html><head>
-<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-<title>Vimpress Local Preview: %(title)s</title>
-<style type="text/css"> ul, li { margin: 1em; } :link,:visited { text-decoration:none } h1,h2,h3,h4,h5,h6,pre,code { font-size:1em; } a img,:link img,:visited img { border:none } body { margin:0 auto; width:770px; font-family: Helvetica, Arial, Sans-serif; font-size:12px; color:#444; }
-</style>
-</meta>
-</head>
-<body>
-%(content)s
-</body>
-</html>
-""" % dict(content = text_html, title = meta["title"])
- with open(os.path.join(vimpress_temp_dir, "vimpress_temp.html"), 'w') as f:
- f.write(html)
- webbrowser.open("file://%s" % f.name)
-
-def blog_wise_open_view():
- """
- Wisely decides whether to wipe out the content of current buffer or open a new splited window.
- """
- if vim.current.buffer.name is None and \
- (vim.eval('&modified') == '0' or \
- len(vim.current.buffer) == 1):
- vim.command('setl modifiable')
- del vim.current.buffer[:]
- vim.command('setl nomodified')
- else:
- vim.command(":new")
- vim.command('setl syntax=blogsyntax')
- vim.command('setl completefunc=Completable')
962 plugin/vimrepress.py
View
@@ -0,0 +1,962 @@
+# -*- coding: utf-8 -*-
+import vim
+import urllib
+import xmlrpclib
+import re
+import os
+import mimetypes
+import webbrowser
+import tempfile
+from ConfigParser import SafeConfigParser
+
+try:
+ import markdown
+except ImportError:
+ try:
+ import markdown2 as markdown
+ except ImportError:
+ class markdown_stub(object):
+ def markdown(self, n):
+ raise VimPressException("The package python-markdown is "
+ "required and is either not present or not properly "
+ "installed.")
+
+ markdown = markdown_stub()
+
+
+def exception_check(func):
+ def __check(*args, **kwargs):
+ try:
+ return func(*args, **kwargs)
+ except (VimPressException, AssertionError), e:
+ echoerr(str(e))
+ except (xmlrpclib.Fault, xmlrpclib.ProtocolError), e:
+ if getattr(e, "faultString", None) is None:
+ echoerr("xmlrpc error: %s" % e)
+ else:
+ echoerr("xmlrpc error: %s" % e.faultString.encode("utf-8"))
+ except IOError, e:
+ echoerr("network error: %s" % e)
+ except Exception, e:
+ echoerr("something wrong: %s" % e)
+ raise
+
+ return __check
+
+echomsg = lambda s: vim.command('echomsg "%s"' % s)
+echoerr = lambda s: vim.command('echoerr "%s"' % s)
+
+################################################
+# Helper Classes
+#################################################
+
+
+class VimPressException(Exception):
+ pass
+
+
+class DataObject(object):
+
+ #CONST
+ DEFAULT_LIST_COUNT = "15"
+ IMAGE_TEMPLATE = '<a href="%(url)s">' \
+ '<img title="%(file)s" alt="%(file)s" src="%(url)s"' \
+ ' class="aligncenter" /></a>'
+ MARKER = dict(bg="=========== Meta ============",
+ mid="=============================",
+ ed="========== Content ==========",
+ more='"====== Press Here for More ======',
+ list_title='"====== '
+ '%(edit_type)s List in %(blog_url)s =========')
+
+ LIST_VIEW_KEY_MAP = dict(enter="<enter>", delete="<delete>")
+ CUSTOM_FIELD_KEY = "mkd_text"
+
+ #Temp variables.
+ __xmlrpc = None
+ __conf_index = 0
+ __config = None
+
+ view = 'edit'
+ vimpress_temp_dir = ''
+
+ blog_username = property(lambda self: self.xmlrpc.username)
+ blog_url = property(lambda self: self.xmlrpc.blog_url)
+ store_markdown = property(lambda self: self.xmlrpc.store_markdown)
+ conf_index = property(lambda self: self.__conf_index)
+ current_post_id = property(lambda self: self.xmlrpc.current_post_id,
+ lambda self, d: setattr(self.xmlrpc, "current_post_id", d))
+ post_cache = property(lambda self: self.xmlrpc.post_cache)
+
+ @property
+ def current_post(self):
+ post_id = self.current_post_id
+ post = self.post_cache.get(post_id)
+ if post is None and post_id == '':
+ post = ContentStruct()
+ if post.post_id != '':
+ self.current_post_id = post.post_id
+ self.current_post = post
+ else:
+ self.post_cache[''] = post
+
+ assert post is not None, "current_post, no way to return None"
+ return post
+
+ @current_post.setter
+ def current_post(self, data):
+ post_id = str(data.post_id)
+
+ # New post, just post first time
+ if self.current_post_id == '' and \
+ post_id != '' and '' in self.post_cache:
+ self.post_cache.pop('')
+ self.current_post_id = post_id
+ if post_id not in self.post_cache:
+ self.post_cache[post_id] = data
+
+ @conf_index.setter
+ def conf_index(self, index):
+ try:
+ index = int(index)
+ except ValueError:
+ raise VimPressException("Invalid Index: %s" % index)
+
+ #auto increase
+ if index < 0:
+ self.__conf_index += 1
+ if self.__conf_index >= len(self.config):
+ self.__conf_index = 0
+ # user enter index
+ else:
+ assert index < len(self.config), "Invalid Index: %d" % index
+ self.__conf_index = index
+
+ self.__xmlrpc = None
+
+ @property
+ def xmlrpc(self):
+ if self.__xmlrpc is None:
+ conf_index = self.conf_index
+ config = self.config[conf_index]
+
+ if "xmlrpc_obj" not in config:
+ try:
+ blog_username = config['username']
+ blog_password = config.get('password', '')
+ blog_url = config['blog_url']
+ store_markdown = config.get('store_markdown', 'y')
+ except KeyError, e:
+ raise VimPressException("Configuration error: %s" % e)
+ echomsg("Connecting to '%s' ... " % blog_url)
+ if blog_password == '':
+ blog_password = vim_input(
+ "Enter password for %s" % blog_url, True)
+ config["xmlrpc_obj"] = wp_xmlrpc(blog_url,
+ blog_username, blog_password, store_markdown)
+
+ self.__xmlrpc = config["xmlrpc_obj"]
+
+ # Setting tags and categories for completefunc
+
+ categories = [i["categoryName"].encode("utf-8")
+ for i in self.xmlrpc.get_categories()]
+ vim.command('let s:completable = "%s"' % '|'.join(categories))
+ echomsg("done.")
+ return self.__xmlrpc
+
+ @property
+ def config(self):
+ if self.__config is None or len(self.__config) == 0:
+
+ confpsr = SafeConfigParser({'store_markdown': 'y'})
+ confile = os.path.expanduser("~/.vimpressrc")
+ conf_options = ("blog_url", "username", "password", "store_markdown")
+
+ if os.path.exists(confile):
+ conf_list = []
+ confpsr.read(confile)
+ for sec in confpsr.sections():
+ values = [confpsr.get(sec, i) for i in conf_options]
+ conf_list.append(dict(zip(conf_options, values)))
+
+ if len(conf_list) > 0:
+ self.__config = conf_list
+ else:
+ raise VimPressException(
+ "You have an empty `~/.vimpressrc', "
+ "thus no configuration will be read. "
+ "If you still have your account info in "
+ "`.vimrc', remove `~/.vimpressrc' and "
+ "try again.")
+
+ else:
+ try:
+ self.__config = vim.eval("VIMPRESS")
+ except vim.error:
+ pass
+ else:
+ # write config to `~/.vimpressrc`,
+ # coding account in .vimrc is obsolesced.
+ if not os.path.exists(confile) and \
+ self.__config is not None:
+ with open(confile, 'w') as f:
+ for i, c in enumerate(self.__config):
+ sec = "Blog%d" % i
+ confpsr.add_section(sec)
+ for i in conf_options:
+ confpsr.set(sec, i, c.get(i, ''))
+ confpsr.write(f)
+
+ echomsg("Your Blog accounts are now copied to "
+ "`~/.vimpressrc', defining account info "
+ "in `.vimrc` is now obsolesced, and may "
+ "lead to secret leak if you share your "
+ "vim configuration with public. Please "
+ "REMOVE the `let VIMPRESS =' code in your "
+ "`.vimrc'. ")
+
+ if self.__config is None or len(self.__config) == 0:
+ raise VimPressException("Could not find vimpress "
+ "configuration. Please read ':help vimpress' "
+ "for more information.")
+
+ return self.__config
+
+
+class wp_xmlrpc(object):
+
+ def __init__(self, blog_url, username, password, store_markdown):
+ self.blog_url = blog_url
+ self.username = username
+ self.password = password
+ self.store_markdown = store_markdown
+ p = xmlrpclib.ServerProxy(os.path.join(blog_url, "xmlrpc.php"))
+ self.mw_api = p.metaWeblog
+ self.wp_api = p.wp
+ self.mt_api = p.mt
+ self.demo_api = p.demo
+
+ assert self.demo_api.sayHello() == "Hello!", \
+ "XMLRPC Error with communication with '%s'@'%s'" % \
+ (username, blog_url)
+
+ self.cache_reset()
+ self.post_cache = dict()
+
+ self.current_post_id = ''
+
+ def cache_reset(self):
+ self.__cache_post_titles = []
+ self.__post_title_max = False
+
+ def cache_remove_post(self, postid):
+ for p in self.__cache_post_titles:
+ if p["postid"] == str(postid):
+ self.__cache_post_titles.remove(p)
+ break
+
+ is_reached_title_max = property(lambda self: self.__post_title_max)
+
+ new_post = lambda self, post_struct: self.mw_api.newPost('',
+ self.username, self.password, post_struct)
+
+ get_post = lambda self, post_id: self.mw_api.getPost(post_id,
+ self.username, self.password)
+
+ edit_post = lambda self, post_id, post_struct: \
+ self.mw_api.editPost(post_id, self.username,
+ self.password, post_struct)
+
+ delete_post = lambda self, post_id: self.mw_api.deletePost('',
+ post_id, self.username, self.password, '')
+
+ def get_recent_post_titles(self, retrive_count=0):
+ if retrive_count > len(self.__cache_post_titles) and \
+ not self.is_reached_title_max:
+ self.__cache_post_titles = self.mt_api.getRecentPostTitles('',
+ self.username, self.password, retrive_count)
+ if len(self.__cache_post_titles) < retrive_count:
+ self.__post_title_max = True
+
+ return self.__cache_post_titles
+
+ get_categories = lambda self: self.mw_api.getCategories('',
+ self.username, self.password)
+
+ new_category = lambda self, category: self.wp_api.newCategory('',
+ self.username, self.password, category)
+
+ new_media_object = lambda self, object_struct: \
+ self.mw_api.newMediaObject('', self.username,
+ self.password, object_struct)
+
+ get_page = lambda self, page_id: self.wp_api.getPage('',
+ page_id, self.username, self.password)
+
+ delete_page = lambda self, page_id: self.wp_api.deletePage('',
+ self.username, self.password, page_id)
+
+ get_page_list = lambda self: self.wp_api.getPageList('',
+ self.username, self.password)
+
+
+class ContentStruct(object):
+
+ buffer_meta = None
+ post_struct_meta = None
+ EDIT_TYPE = ''
+
+ @property
+ def META_TEMPLATE(self):
+ KEYS_BASIC = ("StrID", "Title", "Slug")
+ KEYS_EXT = ("Cats", "Tags")
+ KEYS_BLOG = ("EditType", "EditFormat", "BlogAddr")
+
+ pt = ['"{k:<6}: {{{t}}}'.format(k=p, t=p.lower()) for p in KEYS_BASIC]
+ if self.EDIT_TYPE == "post":
+ pt.extend(['"{k:<6}: {{{t}}}'.format(k=p, t=p.lower())
+ for p in KEYS_EXT])
+ pm = "\n".join(pt)
+ bm = "\n".join(['"{k:<11}: {{{t}}}'.format(k=p, t=p.lower())
+ for p in KEYS_BLOG])
+ return u'"{bg}\n{0}\n"{mid}\n{1}\n"{ed}\n'.format(pm, bm, **G.MARKER)
+
+ POST_BEGIN = property(lambda self: len(self.META_TEMPLATE.splitlines()))
+ raw_text = ''
+ html_text = ''
+
+ def __init__(self, edit_type=None, post_id=None):
+
+ self.EDIT_TYPE = edit_type
+ self.buffer_meta = dict(strid='', edittype=edit_type,
+ blogaddr=g_data.blog_url)
+
+ self.post_struct_meta = dict(title='',
+ wp_slug='',
+ post_type=edit_type,
+ description='',
+ custom_fields=[],
+ post_status='draft')
+
+ if post_id is not None:
+ self.refresh_from_wp(post_id)
+
+ if self.EDIT_TYPE is None:
+ self.parse_buffer()
+
+ def parse_buffer(self):
+ start = 0
+ while not vim.current.buffer[start][1:].startswith(G.MARKER['bg']):
+ start += 1
+
+ end = start + 1
+ while not vim.current.buffer[end][1:].startswith(G.MARKER['ed']):
+ if not vim.current.buffer[end].startswith('"===='):
+ line = vim.current.buffer[end][1:].strip().split(":")
+ k, v = line[0].strip().lower(), ':'.join(line[1:])
+ self.buffer_meta[k.strip().lower()] = v.strip().decode('utf-8')
+ end += 1
+
+ if self.EDIT_TYPE != self.buffer_meta["edittype"]:
+ self.EDIT_TYPE = self.buffer_meta["edittype"]
+
+ self.buffer_meta["content"] = '\n'.join(
+ vim.current.buffer[end + 1:]).decode('utf-8')
+
+ def fill_buffer(self):
+ meta = dict(strid="", title="", slug="",
+ cats="", tags="", editformat="Markdown", edittype="")
+ meta.update(self.buffer_meta)
+ meta_text = self.META_TEMPLATE.format(**meta)\
+ .encode('utf-8').splitlines()
+ vim.current.buffer[0] = meta_text[0]
+ vim.current.buffer.append(meta_text[1:])
+ content = self.buffer_meta.get("content", ' ')\
+ .encode('utf-8').splitlines()
+ vim.current.buffer.append(content)
+
+ def update_buffer_meta(self):
+ """
+ Updates the meta data region of a blog editing buffer.
+ @params **kwargs - keyworded arguments
+ """
+ kw = self.buffer_meta
+ start = 0
+ while not vim.current.buffer[start][1:].startswith(G.MARKER['bg']):
+ start += 1
+
+ end = start + 1
+ while not vim.current.buffer[end][1:].startswith(G.MARKER['ed']):
+ if not vim.current.buffer[end].startswith('"===='):
+ line = vim.current.buffer[end][1:].strip().split(":")
+ k, v = line[0].strip().lower(), ':'.join(line[1:])
+ if k in kw:
+ new_line = u"\"%s: %s" % (line[0], kw[k])
+ vim.current.buffer[end] = new_line.encode('utf-8')
+ end += 1
+
+ def refresh_from_buffer(self):
+ self.parse_buffer()
+
+ meta = self.buffer_meta
+ struct = self.post_struct_meta
+
+ struct.update(title=meta["title"],
+ wp_slug=meta["slug"], post_type=self.EDIT_TYPE)
+
+ if self.EDIT_TYPE == "post":
+ struct.update(categories=meta["cats"].split(','),
+ mt_keywords=meta["tags"].split(','))
+
+ self.rawtext = rawtext = meta["content"]
+
+ #Translate markdown and save in custom fields.
+ if meta["editformat"].lower() == "markdown":
+ if g_data.store_markdown == 'y':
+ # Store markdown in custom field only when enabled
+ for f in struct["custom_fields"]:
+ if f["key"] == G.CUSTOM_FIELD_KEY:
+ f["value"] = rawtext
+ break
+ ## Not found, add new custom field.
+ else:
+ field = dict(key=G.CUSTOM_FIELD_KEY, value=rawtext)
+ struct["custom_fields"].append(field)
+
+ struct["description"] = self.html_text = markdown.markdown(rawtext)
+ else:
+ struct["description"] = self.html_text = rawtext
+
+ def refresh_from_wp(self, post_id):
+ # get from wp
+ self.post_struct_meta = struct = getattr(g_data.xmlrpc,
+ "get_" + self.EDIT_TYPE)(post_id)
+
+ # struct buffer meta
+ meta = dict(editformat="HTML",
+ title=struct["title"],
+ slug=struct["wp_slug"])
+
+ if self.EDIT_TYPE == "post":
+ meta.update(strid=str(struct["postid"]),
+ cats=", ".join(struct["categories"]),
+ tags=struct["mt_keywords"])
+ MORE_KEY = "mt_text_more"
+ else:
+ meta.update(strid=str(struct["page_id"]))
+ MORE_KEY = "text_more"
+
+ self.html_text = content = struct["description"]
+
+ #detect more text
+ post_more = struct.get(MORE_KEY, '')
+ if len(post_more) > 0:
+ content += u'<!--more-->' + post_more
+ struct[MORE_KEY] = ''
+ self.html_text = struct["description"] = content
+
+ #Use Markdown text if exists in custom fields
+ for field in struct["custom_fields"]:
+ if field["key"] == G.CUSTOM_FIELD_KEY:
+ meta['editformat'] = "Markdown"
+ self.raw_text = content = field["value"]
+ vim.command('setl syntax=md_blogsyntax')
+ break
+ else:
+ self.raw_text = content
+ vim.command('setl syntax=html_blogsyntax')
+
+ meta["content"] = content
+
+ self.buffer_meta.update(meta)
+
+ def save_post(self):
+ ps = self.post_struct_meta
+ if self.EDIT_TYPE == "post":
+ if ps.get("postid", '') == '' and self.post_id == '':
+ post_id = g_data.xmlrpc.new_post(ps)
+ else:
+ post_id = ps["postid"] if "postid" in ps \
+ else int(self.post_id)
+ g_data.xmlrpc.edit_post(post_id, ps)
+ else:
+ if ps.get("page_id", '') == '' and self.post_id == '':
+ post_id = g_data.xmlrpc.new_post(ps)
+ else:
+ post_id = ps["page_id"] if "page_id" in ps \
+ else int(self.post_id)
+ g_data.xmlrpc.edit_post(post_id, ps)
+
+ self.refresh_from_wp(post_id)
+
+ post_status = property(lambda self:
+ self.post_struct_meta[self.EDIT_TYPE + "_status"])
+
+ @post_status.setter
+ def post_status(self, data):
+ if data is not None:
+ self.post_struct_meta[self.EDIT_TYPE + "_status"] = data
+
+ post_id = property(lambda self: self.buffer_meta["strid"])
+
+ def html_preview(self):
+ """
+ Opens a browser with a local preview of the content.
+ @params text_html - the html content
+ meta - a dictionary of the meta data
+ """
+ if g_data.vimpress_temp_dir == '':
+ g_data.vimpress_temp_dir = tempfile.mkdtemp(suffix="vimpress")
+
+ html = \
+ u"""<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Vimpress Local Preview: %(title)s</title> <style type="text/css"> ul, li { margin: 1em; } :link,:visited { text-decoration:none } h1,h2,h3,h4,h5,h6,pre,code { font-size:1.1em; } h1 {font-size: 1.8em;} h2 {font-size: 1.5em;} h3{font-size: 1.3em;} h4{font-size: 1.2em;} h5 {font-size: 1.1em;} a img,:link img,:visited img { border:none } body { margin:0 auto; width:770px; font-family: Helvetica, Arial, Sans-serif; font-size:12px; color:#444; } </style> </meta> </head> <body> %(content)s </body> </html>
+""" % dict(content=self.html_text, title=self.buffer_meta["title"])
+ with open(os.path.join(
+ g_data.vimpress_temp_dir, "vimpress_temp.html"), 'w') as f:
+ f.write(html.encode('utf-8'))
+ webbrowser.open("file://%s" % f.name)
+
+ def remote_preview(self, pub="draft"):
+ self.post_status = pub
+ self.save_post()
+ webbrowser.open("%s?p=%s&preview=true" %
+ (g_data.blog_url, self.post_id))
+
+
+#################################################
+# Golbal Variables
+#################################################
+G = DataObject
+g_data = DataObject()
+
+#################################################
+# Helper Functions
+#################################################
+
+
+def vim_encoding_check(func):
+ """
+ Decorator.
+ Check vim environment. wordpress via xmlrpc only support unicode data,
+ setting vim to utf-8 for all data compatible.
+ """
+ def __check(*args, **kw):
+ orig_enc = vim.eval("&encoding")
+ if orig_enc is None:
+ echomsg("Failed to detech current vim encoding. Make sure your"
+ " content encoded in UTF-8 to have vimpress work "
+ "correctly.")
+ elif orig_enc != "utf-8":
+ modified = vim.eval("&modified")
+ buf_list = '\n'.join(vim.current.buffer).decode(orig_enc).encode('utf-8').splitlines()
+ del vim.current.buffer[:]
+ vim.command("setl encoding=utf-8")
+ vim.current.buffer[0] = buf_list[0]
+ if len(buf_list) > 1:
+ vim.current.buffer.append(buf_list[1:])
+ if modified == '0':
+ vim.command('setl nomodified')
+ return func(*args, **kw)
+ return __check
+
+
+def view_switch(view = "", assert_view = "", reset = False):
+ """
+ Decorator.
+ For commands to switch between edit/list view, data/status need to be configured.
+ """
+ def switch(func):
+ def __run(*args, **kw):
+ if assert_view != '':
+ if g_data.view != assert_view:
+ raise VimPressException("Command only available at '%s' view." % assert_view)
+
+ if func.func_name == "blog_new":
+ if g_data.view == "list":
+ kw["currentContent"] = ['']
+ else:
+ kw["currentContent"] = vim.current.buffer[:]
+ elif func.func_name == "blog_config_switch":
+ if g_data.view == "list":
+ kw["refresh_list"] = True
+
+ if reset:
+ g_data.xmlrpc.cache_reset()
+
+ if view != '':
+ #Switching view
+ if g_data.view != view:
+
+ #from list view
+ if g_data.view == "list":
+ for v in g_data.LIST_VIEW_KEY_MAP.values():
+ if vim.eval("mapcheck('%s')" % v):
+ vim.command('unmap <buffer> %s' % v)
+
+ g_data.view = view
+
+ return func(*args, **kw)
+ return __run
+ return switch
+
+
+def blog_wise_open_view():
+ """
+ Wisely decides whether to wipe out the content of current buffer or open a new splited window.
+ """
+ if vim.current.buffer.name is None and \
+ (vim.eval('&modified') == '0' or
+ len(vim.current.buffer) == 1):
+ vim.command('setl modifiable')
+ del vim.current.buffer[:]
+ vim.command('setl nomodified')
+ else:
+ vim.command(":new")
+ vim.command('setl syntax=md_blogsyntax')
+ vim.command('setl completefunc=vimrepress#CateComplete')
+
+
+@vim_encoding_check
+def vim_input(message = 'input', secret = False):
+ vim.command('call inputsave()')
+ vim.command("let s:user_input = %s('%s :')" % (("inputsecret" if secret else "input"), message))
+ vim.command('call inputrestore()')
+ return vim.eval('s:user_input')
+
+
+#################################################
+# Command Functions
+#################################################
+
+@exception_check
+@vim_encoding_check
+@view_switch(assert_view = "edit", reset = True)
+def blog_save(pub = None):
+ """
+ Saves the current editing buffer.
+ @params pub - either "draft" or "publish"
+ """
+ if pub not in ("publish", "draft", None):
+ raise VimPressException(":BlogSave draft|publish")
+ cp = g_data.current_post
+ assert cp is not None, "Can't get current post obj."
+ cp.refresh_from_buffer()
+
+ if cp.buffer_meta["blogaddr"] != g_data.blog_url and cp.post_id != '':
+ confirm = vim_input("Are u sure saving it to \"%s\" ? BlogAddr in current buffer does NOT matched. \nStill saving it ? (may cause data lost) [yes/NO]" % g_data.blog_url)
+ assert confirm.lower() == 'yes', "Aborted."
+
+ cp.post_status = pub
+ cp.save_post()
+ cp.update_buffer_meta()
+ g_data.current_post = cp
+ notify = "%s ID=%s saved with status '%s'" % (cp.post_status, cp.post_id, cp.post_status)
+ echomsg(notify)
+ vim.command('setl nomodified')
+
+
+@exception_check
+@vim_encoding_check
+@view_switch(view = "edit")
+def blog_new(edit_type = "post", currentContent = None):
+ """
+ Creates a new editing buffer of specified type.
+ @params edit_type - either "post" or "page"
+ """
+ if edit_type.lower() not in ("post", "page", "category"):
+ raise VimPressException("Invalid option: %s " % edit_type)
+
+ if edit_type.lower() == "category":
+ category_name = vim_input("New Category name")
+ category_slug = vim_input("Category slug (optional)")
+ ret = g_data.xmlrpc.new_category(dict(name = category_name, slug = category_slug))
+
+ if type(ret) is int:
+ echomsg("Category '%s' created with ID %d. Updating local cache ... "
+ % (category_name, ret))
+ categories = [i["categoryName"].encode("utf-8")
+ for i in g_data.xmlrpc.get_categories()]
+ vim.command('let s:completable = "%s"' % '|'.join(categories))
+ echomsg("Done.")
+ else:
+ echoerr("Category create ERROR: " + str(ret))
+
+ else:
+ blog_wise_open_view()
+ g_data.current_post = ContentStruct(edit_type = edit_type)
+ cp = g_data.current_post
+ cp.fill_buffer()
+
+
+@view_switch(view = "edit")
+def blog_edit(edit_type, post_id):
+ """
+ Opens a new editing buffer with blog content of specified type and id.
+ @params edit_type - either "post" or "page"
+ post_id - the id of the post or page
+ """
+ blog_wise_open_view()
+ if edit_type.lower() not in ("post", "page"):
+ raise VimPressException("Invalid option: %s " % edit_type)
+ post_id = str(post_id)
+
+ if post_id in g_data.post_cache:
+ cp = g_data.current_post = g_data.post_cache[post_id]
+ else:
+ cp = g_data.current_post = ContentStruct(edit_type = edit_type, post_id = post_id)
+
+ cp.fill_buffer()
+ vim.current.window.cursor = (cp.POST_BEGIN, 0)
+ vim.command('setl nomodified')
+ vim.command('setl textwidth=0')
+ for v in G.LIST_VIEW_KEY_MAP.values():
+ if vim.eval("mapcheck('%s')" % v):
+ vim.command('unmap <buffer> %s' % v)
+
+
+@view_switch(assert_view = "list")
+def blog_delete(edit_type, post_id):
+ """
+ Deletes a page or post of specified id.
+ @params edit_type - either "page" or "post"
+ post_id - the id of the post or page
+ """
+ if edit_type.lower() not in ("post", "page"):
+ raise VimPressException("Invalid option: %s " % edit_type)
+ deleted = getattr(g_data.xmlrpc, "delete_" + edit_type)(post_id)
+ assert deleted is True, "There was a problem deleting the %s." % edit_type
+ echomsg("Deleted %s id %s. " % (edit_type, str(post_id)))
+ g_data.xmlrpc.cache_remove_post(post_id)
+ blog_list(edit_type)
+
+
+@exception_check
+@view_switch(assert_view = "list")
+def blog_list_on_key_press(action, edit_type):
+ """
+ Calls blog open on the current line of a listing buffer.
+ """
+ if action.lower() not in ("open", "delete"):
+ raise VimPressException("Invalid option: %s" % action)
+
+ row = vim.current.window.cursor[0]
+ line = vim.current.buffer[row - 1]
+ id = line.split()[0]
+ title = line[len(id):].strip()
+
+ try:
+ int(id)
+ except ValueError:
+ if line.find("More") != -1:
+ assert g_data.xmlrpc.is_reached_title_max is False, "No more posts."
+ vim.command("setl modifiable")
+ del vim.current.buffer[len(vim.current.buffer) - 1:]
+ append_blog_list(edit_type)
+ vim.current.buffer.append(G.MARKER['more'])
+ vim.command("setl nomodified")
+ vim.command("setl nomodifiable")
+ return
+ else:
+ raise VimPressException("Move cursor to a post/page line and press Enter.")
+
+ if len(title) > 30:
+ title = title[:30] + ' ...'
+
+ if action.lower() == "delete":
+ confirm = vim_input("Confirm Delete [%s]: %s? [yes/NO]" % (id, title))
+ assert confirm.lower() == 'yes', "Delete Aborted."
+
+ vim.command("setl modifiable")
+ del vim.current.buffer[:]
+ vim.command("setl nomodified")
+
+ if action == "open":
+ blog_edit(edit_type, int(id))
+ elif action == "delete":
+ blog_delete(edit_type, int(id))
+
+
+def append_blog_list(edit_type, count = G.DEFAULT_LIST_COUNT):
+ if edit_type.lower() == "post":
+ current_posts = len(vim.current.buffer) - 1
+ retrive_count = int(count) + current_posts
+ posts_titles = g_data.xmlrpc.get_recent_post_titles(retrive_count)
+
+ vim.current.buffer.append(
+ [(u"%(postid)s\t%(title)s" % p).encode('utf-8')
+ for p in posts_titles[current_posts:]])
+ else:
+ pages = g_data.xmlrpc.get_page_list()
+ vim.current.buffer.append(
+ [(u"%(page_id)s\t%(page_title)s" % p).encode('utf-8') for p in pages])
+
+
+@exception_check
+@vim_encoding_check
+@view_switch(view = "list")
+def blog_list(edit_type = "post", keep_type = False):
+ """
+ Creates a listing buffer of specified type.
+ @params edit_type - either "post(s)" or "page(s)"
+ """
+ if keep_type:
+ first_line = vim.current.buffer[0]
+ assert first_line.find("List") != -1, "Failed to detect current list type."
+ edit_type = first_line.split()[1].lower()
+
+ blog_wise_open_view()
+ vim.current.buffer[0] = G.MARKER["list_title"] % \
+ dict(edit_type = edit_type.capitalize(), blog_url = G.blog_url)
+
+ if edit_type.lower() not in ("post", "page"):
+ raise VimPressException("Invalid option: %s " % edit_type)
+
+ append_blog_list(edit_type, G.DEFAULT_LIST_COUNT)
+
+ if edit_type == "post":
+ vim.current.buffer.append(G.MARKER['more'])
+
+ vim.command("setl nomodified")
+ vim.command("setl nomodifiable")
+ vim.current.window.cursor = (2, 0)
+ vim.command("map <silent> <buffer> %(enter)s :py blog_list_on_key_press('open', '%%s')<cr>"
+ % G.LIST_VIEW_KEY_MAP % edit_type)
+ vim.command("map <silent> <buffer> %(delete)s :py blog_list_on_key_press('delete', '%%s')<cr>"
+ % G.LIST_VIEW_KEY_MAP % edit_type)
+ echomsg("Press <Enter> to edit. <Delete> to move to trash.")
+
+
+@exception_check
+@vim_encoding_check
+@view_switch(assert_view = "edit")
+def blog_upload_media(file_path):
+ """
+ Uploads a file to the blog.
+ @params file_path - the file's path
+ """
+ if not os.path.exists(file_path):
+ raise VimPressException("File does not exist: %s" % file_path)
+
+ name = os.path.basename(file_path)
+ filetype = mimetypes.guess_type(file_path)[0]
+ with open(file_path) as f:
+ bits = xmlrpclib.Binary(f.read())
+
+ result = g_data.xmlrpc.new_media_object(dict(name = name, type = filetype, bits = bits))
+
+ ran = vim.current.range
+ if filetype.startswith("image"):
+ img = G.IMAGE_TEMPLATE % result
+ ran.append(img)
+ else:
+ ran.append(result["url"])
+ ran.append('')
+
+
+@exception_check
+@vim_encoding_check
+@view_switch(assert_view = "edit")
+def blog_append_code(code_type = ""):
+ html = \
+"""<pre lang="%s"%s>
+</pre>"""
+ if code_type == "":
+ code_type = ("text", "")
+ else:
+ code_type = (code_type, ' line="1"')
+ html = html % code_type
+ row, col = vim.current.window.cursor
+ code_block = html.splitlines()
+ vim.current.range.append(code_block)
+ vim.current.window.cursor = (row + len(code_block), 0)
+
+
+@exception_check
+@vim_encoding_check
+@view_switch(assert_view = "edit")
+def blog_preview(pub = "local"):
+ """
+ Opens a browser window displaying the content.
+ @params pub - If "local", the content is shown in a browser locally.
+ If "draft", the content is saved as a draft and previewed remotely.
+ If "publish", the content is published and displayed remotely.
+ """
+ cp = g_data.current_post
+ cp.refresh_from_buffer()
+ if pub == "local":
+ cp.html_preview()
+ elif pub in ("publish", "draft"):
+ cp.remote_preview(pub)
+ if pub == "draft":
+ echomsg("You have to login in the browser to preview the post when save as draft.")
+ else:
+ raise VimPressException("Invalid option: %s " % pub)
+
+
+@exception_check
+def blog_guess_open(what):
+ """
+ Tries several methods to get the post id from different user inputs, such as args, url, postid etc.
+ """
+ post_id = ''
+ blog_index = -1
+ if type(what) is str:
+
+ if what.startswith(g_data.blog_url):
+ blog_index = 1
+
+ # User input a url contained in the profiles
+ if blog_index != -1:
+ guess_id = re.search(r"\S+?p=(\d+)$", what)
+
+ # permalinks
+ if guess_id is None:
+
+ # try again for /archives/%post_id%
+ guess_id = re.search(r"\S+/archives/(\d+)", what)
+
+ # fail, try get full link from headers
+ if guess_id is None:
+ headers = urllib.urlopen(what).headers.headers
+ for link in headers:
+ if link.startswith("Link:"):
+ post_id = re.search(r"<\S+?p=(\d+)>", link).group(1)
+
+ else:
+ post_id = guess_id.group(1)
+
+ # full link with ID (http://blog.url/?p=ID)
+ else:
+ post_id = guess_id.group(1)
+
+ # detected something ?
+ assert post_id != '', "Failed to get post/page id from '%s'." % what
+
+ #switch view if needed.
+ if blog_index != -1 and blog_index != g_data.conf_index:
+ blog_config_switch(blog_index)
+
+ # User input not a url, try numeric
+ else:
+ try:
+ post_id = str(int(what))
+ except ValueError:
+ raise VimPressException("Failed to get post/page id from '%s'." % what)
+
+ blog_edit("post", post_id)
+
+
+@exception_check
+@vim_encoding_check
+@view_switch()
+def blog_config_switch(index = -1, refresh_list = False):
+ """
+ Switches the blog to the 'index' of the configuration array.
+ """
+ g_data.conf_index = index
+ if refresh_list:
+ blog_list(keep_type = True)
+ echomsg("Vimpress switched to '%s'@'%s'" % (g_data.blog_username, g_data.blog_url))
+
+
118 plugin/vimrepress.vim
View
@@ -0,0 +1,118 @@
+"#######################################################################
+" Copyright (C) 2007 Adrien Friggeri.
+"
+" 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 2, or (at your option)
+" any later version.
+"
+" This program is distributed in the hope that it will be useful,
+" but WITHOUT ANY WARRANTY; without even the implied warranty of
+" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+" GNU General Public License for more details.
+"
+" You should have received a copy of the GNU General Public License
+" along with this program; if not, write to the Free Software Foundation,
+" Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+"
+" Contributors: Adrien Friggeri <adrien@friggeri.net>
+" Pigeond <http://pigeond.net/blog/>
+" Justin Sattery <justin.slattery@fzysqr.com>
+" Lenin Lee <lenin.lee@gmail.com>
+" Conner McDaniel <connermcd@gmail.com>
+"
+" Forked By: Preston M.[BOYPT] <pentie@gmail.com>
+" Repository: https://bitbucket.org/pentie/vimrepress
+"
+" URL: http://www.friggeri.net/projets/vimblog/
+" http://pigeond.net/blog/2009/05/07/vimpress-again/
+" http://pigeond.net/git/?p=vimpress.git
+" http://fzysqr.com/
+" http://apt-blog.net
+"
+" VimRepress
+" - A mod of a mod of a mod of Vimpress.
+" - A vim plugin fot writting your wordpress blog.
+" - Write with Markdown, control posts format precisely.
+" - Stores Markdown rawtext in wordpress custom fields.
+"
+" Version: 3.2.1
+"
+" Config: Create account configure as `~/.vimpressrc' in the following
+" format:
+"
+"[Blog0]
+"blog_url = http://a-blog.com/
+"username = admin
+"password = 123456
+"
+"[Blog1]
+"blog_url = https://someone.wordpress.com/
+"username = someone
+"password =
+"
+"#######################################################################
+
+if !has("python")
+ finish
+endif
+
+let s:py_loaded = 0
+let s:vimpress_dir = fnamemodify(expand("<sfile>"), ":p:h")
+
+function! s:CompSave(ArgLead, CmdLine, CursorPos)
+ return "publish\ndraft\n"
+endfunction
+
+function! s:CompPrev(ArgLead, CmdLine, CursorPos)
+ return "local\npublish\ndraft\n"
+endfunction
+
+function! s:CompEditType(ArgLead, CmdLine, CursorPos)
+ return "post\npage\n"
+endfunction
+
+function! s:CompNewType(ArgLead, CmdLine, CursorPos)
+ return "post\npage\ncategory\n"
+endfunction
+
+function! vimrepress#CateComplete(findstart, base)
+ if a:findstart
+ " locate the start of the word
+ let line = getline('.')
+ let start = col('.') - 1
+ while start > 0 && line[start - 1] =~ '\a'
+ let start -= 1
+ endwhile
+ return start
+ else
+ " find matching items
+ let res = []
+ for m in split(s:completable,"|")
+ if m =~ '^' . a:base
+ call add(res, m)
+ endif
+ endfor
+ return res
+ endif
+endfun
+
+function! s:PyCMD(pyfunc)
+ if (s:py_loaded == 0)
+ exec("cd " . s:vimpress_dir)
+ let s:pyfile = fnamemodify("vimrepress.py", ":p")
+ exec("cd -")
+ exec("pyfile " . s:pyfile)
+ let s:py_loaded = 1
+ endif
+ exec('python ' . a:pyfunc)
+endfunction
+
+command! -nargs=? -complete=custom,s:CompEditType BlogList call s:PyCMD('blog_list(<f-args>)')
+command! -nargs=? -complete=custom,s:CompNewType BlogNew call s:PyCMD('blog_new(<f-args>)')
+command! -nargs=? -complete=custom,s:CompSave BlogSave call s:PyCMD('blog_save(<f-args>)')
+command! -nargs=? -c