From a32f801819ca54a3bbca85a7e425cdc78cb9fe84 Mon Sep 17 00:00:00 2001 From: Preston M Date: Tue, 19 Jun 2012 09:42:12 +0800 Subject: [PATCH 01/13] version 3.2.0 --- README | 41 -- README.md | 48 ++ doc/vimpress.txt | 90 +--- plugin/blog.vim | 1288 ++++++++++++++++++++++++++-------------------- 4 files changed, 805 insertions(+), 662 deletions(-) delete mode 100644 README create mode 100644 README.md diff --git a/README b/README deleted file mode 100644 index 649c28e..0000000 --- a/README +++ /dev/null @@ -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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..f142dcf --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +#Welcome +VimRepress is a plugin for managing wordpress blog from Vim, using Markdown syntax. + +This is the release mirror repo of: https://bitbucket.org/pentie/vimrepress + +##Features + * NEW/EDIT/DELETE wordpress Posts/Pages. + * In both Markdown / HTML format. + * Markdown text 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
 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
+
+    [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.
+
+###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`.
+
diff --git a/doc/vimpress.txt b/doc/vimpress.txt
index da86696..88c6725 100644
--- a/doc/vimpress.txt
+++ b/doc/vimpress.txt
@@ -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). 
diff --git a/plugin/blog.vim b/plugin/blog.vim
index 02c4d48..41ae7e9 100644
--- a/plugin/blog.vim
+++ b/plugin/blog.vim
@@ -15,35 +15,41 @@
 " 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 
+" Contributors:	Adrien Friggeri 
 "               Pigeond 
 "               Justin Sattery 
 "               Lenin Lee 
 "               Conner McDaniel 
 "
-"     Forked:   Preston M.[BOYPT] 
+" Forked By: Preston M.[BOYPT] 
+" 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://apt-blog.net
 "           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.0
 "
-" Version:	2.1.5
+" Config: Create account configure as `~/.vimpressrc' in the following
+" format:
 "
-" Configure: Add blog configure into your .vimrc (password optional)
+"[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 =
 "
 "#######################################################################
 
@@ -63,28 +69,7 @@ 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()')
+command! -nargs=? -complete=custom,CompEditType BlogList exec('py blog_list()')
 command! -nargs=? -complete=custom,CompEditType BlogNew exec('py blog_new()')
 command! -nargs=? -complete=custom,CompSave BlogSave exec('py blog_save()')
 command! -nargs=? -complete=custom,CompPrev BlogPreview exec('py blog_preview()')
@@ -95,7 +80,16 @@ command! -nargs=? BlogCode exec('py blog_append_code()')
 
 python << EOF
 # -*- coding: utf-8 -*-
-import urllib, urllib2, vim, xml.dom.minidom, xmlrpclib, sys, string, re, os, mimetypes, webbrowser, tempfile, time
+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:
@@ -104,183 +98,532 @@ except ImportError:
     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.")
+                raise VimPressException("The package python-markdown is "
+                        "required and is either not present or not properly "
+                        "installed.")
+
         markdown = markdown_stub()
 
-image_template = '%(file)s'
-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 = "", delete = "")
+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
 
-tag_string = ""
-tag_re = re.compile(tag_string % dict(url = '(?P\S+)', file = '(?P\S+)'))
+echomsg = lambda s: vim.command('echomsg "%s"' % s)
+echoerr = lambda s: vim.command('echoerr "%s"' % s)
+
+################################################
+# Helper Classes
+#################################################
 
-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
-    """
+class DataObject(object):
+
+    #CONST
+    DEFAULT_LIST_COUNT = "15"
+    IMAGE_TEMPLATE = '' \
+                     '%(file)s'
+    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="", 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)
+    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
 
-    attach = dict()
-    try:
-        lead = post.rindex("' + 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"]
+                break
+        else:
+            self.raw_text = content
+
+        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""" Vimpress Local Preview: %(title)s     %(content)s  
+""" % 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
+#################################################
 
-    return __check
 
-def __vim_encoding_check(func):
+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 != "utf-8":
+        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').split('\n')
+            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]
@@ -291,230 +634,169 @@ def __vim_encoding_check(func):
         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"):
+def view_switch(view = "", assert_view = "", reset = False):
     """
-    Saves the current editing buffer.
-    @params pub - either "draft" or "publish"
+    Decorator.
+    For commands to switch between edit/list view, data/status need to be configured.
     """
-    if vimpress_view != 'edit':
-        raise VimPressException("Command not available at list view.")
-    if pub not in ("publish", "draft"):
-        raise VimPressException(":BlogSave draft|publish")
+    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)
 
-    is_publish = (pub == "publish")
+            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
 
-    meta = blog_meta_parse()
-    rawtext = '\n'.join(vim.current.buffer[meta["post_begin"]:])
+            if reset:
+                g_data.xmlrpc.cache_reset()
 
-    #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')
+            if view != '':
+                #Switching view
+                if g_data.view != view:
 
-        # Add tag string at the last of the post.
-        text += tag_string % attach
-    else:
-        text = rawtext
+                    #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  %s' % v)
 
-    edit_type = meta["edittype"]
-    strid = meta["strid"] 
+                    g_data.view = view
 
-    if edit_type.lower() not in ("post", "page"):
-        raise VimPressException(
-                "Fail to work with edit type %s " % edit_type)
+            return func(*args, **kw)
+        return __run
+    return switch
 
-    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
+
+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:
-        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)
+        vim.command(":new")
+    vim.command('setl syntax=blogsyntax')
+    vim.command('setl completefunc=Completable')
+
+
+@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')
 
-        notify = "%s edited and %s.   ID=%s" % \
-                (edit_type.capitalize(), "published" if is_publish else "saved as a draft", strid)
 
-    sys.stdout.write(notify)
+#################################################
+# 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')
 
-    return meta
 
-@__exception_check
-@__vim_encoding_check
-@__xmlrpc_api_check
-def blog_new(edit_type = "post"):
+@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"
     """
-    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  %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')
+    g_data.current_post = ContentStruct(edit_type = edit_type)
+    cp = g_data.current_post
+    cp.fill_buffer()
 
-@__xmlrpc_api_check
+
+@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
     """
-    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)
+    post_id = str(post_id)
 
-    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 += '' + post_more
-    elif len(page_more) > 0:
-        content += '' + 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
+    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)
 
-    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)
+    cp.fill_buffer()
+    vim.current.window.cursor = (cp.POST_BEGIN, 0)
     vim.command('setl nomodified')
     vim.command('setl textwidth=0')
-
-    for v in list_view_key_map.values():
+    for v in G.LIST_VIEW_KEY_MAP.values():
         if vim.eval("mapcheck('%s')" % v):
             vim.command('unmap  %s' % v)
 
-@__xmlrpc_api_check
+
+@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
     """
-    global vimpress_view
     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)
 
-    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):
+@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.
     """
-    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]
@@ -523,79 +805,93 @@ def blog_list_on_key_press(action):
     try:
         int(id)
     except ValueError:
-        raise VimPressException("Move cursor to a post/page line and press KEY.")
+        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))
-        if confirm != 'yes':
-            sys.stdout.write("Delete Aborted.\n")
-            return
+        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 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"):
+
+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)"
-            count     - number to show (only for posts)
     """
-    global vimpress_view
-    vimpress_view = 'list'
+    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] = "\"====== List of %ss in %s =========" % (edit_type.capitalize(), blog_url)
+    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", "posts", "page", "pages"):
+    if edit_type.lower() not in ("post", "page"):
         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])
+    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   %(enter)s :py blog_list_on_key_press('open')" % list_view_key_map)
-    vim.command("map   %(delete)s :py blog_list_on_key_press('delete')" % list_view_key_map)
-    sys.stdout.write("Press  to edit.  to move to trash.\n")
+    vim.command("map   %(enter)s :py blog_list_on_key_press('open', '%%s')"
+            % G.LIST_VIEW_KEY_MAP % edit_type)
+    vim.command("map   %(delete)s :py blog_list_on_key_press('delete', '%%s')"
+            % G.LIST_VIEW_KEY_MAP % edit_type)
+    echomsg("Press  to edit.  to move to trash.")
+
 
-@__exception_check
-@__vim_encoding_check
-@__xmlrpc_api_check
+@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 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)
 
@@ -604,37 +900,38 @@ def blog_upload_media(file_path):
     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))
+    result = g_data.xmlrpc.new_media_object(dict(name = name, type = filetype, bits = bits))
 
     ran = vim.current.range
     if filetype.startswith("image"):
-        img = image_template % result
+        img = G.IMAGE_TEMPLATE % result
         ran.append(img)
     else:
         ran.append(result["url"])
     ran.append('')
 
-@__exception_check
-@__vim_encoding_check
+
+@exception_check
+@vim_encoding_check
+@view_switch(assert_view = "edit")
 def blog_append_code(code_type = ""):
-    if vimpress_view != 'edit':
-        raise VimPressException("Command not available at list view.")
     html = \
-"""
+"""
 
""" - if code_type != "": - args = 'lang="%s" line="1"' % code_type + if code_type == "": + code_type = ("text", "") else: - args = 'lang="text"' - - row, col = vim.current.window.cursor - code_block = (html % args).split('\n') + 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 + +@exception_check +@vim_encoding_check +@view_switch(assert_view = "edit") def blog_preview(pub = "local"): """ Opens a browser window displaying the content. @@ -642,35 +939,23 @@ def blog_preview(pub = "local"): 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"]:]) - + cp = g_data.current_post + cp.refresh_from_buffer() 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) + cp.html_preview() + elif pub in ("publish", "draft"): + cp.remote_preview(pub) if pub == "draft": - sys.stdout.write("\nYou have to login in the browser to preview the post when save as 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 +@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: @@ -696,9 +981,6 @@ def blog_guess_open(what): 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) @@ -706,125 +988,31 @@ def blog_guess_open(what): 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) + # Uesr input something not a usabe url, try numberic else: try: post_id = str(int(what)) except ValueError: - pass + raise VimPressException("Failed to get post/page id from '%s'." % what) - # 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 = \ -""" - - -Vimpress Local Preview: %(title)s - - - - -%(content)s - - -""" % 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(): +@exception_check +@vim_encoding_check +@view_switch() +def blog_config_switch(index = -1, refresh_list = False): """ - Wisely decides whether to wipe out the content of current buffer or open a new splited window. + Switches the blog to the 'index' of the configuration array. """ - 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') + 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)) From 0fea160c11f724cc2b866b78d841b8dd2d32e9d2 Mon Sep 17 00:00:00 2001 From: Preston M Date: Tue, 19 Jun 2012 09:51:34 +0800 Subject: [PATCH 02/13] fix readme format --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f142dcf..b4d98f0 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ This is the release mirror repo of: https://bitbucket.org/pentie/vimrepress * 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
 element]
+ * BlogCode     [type of lang for the \ element]
  
   (Commands with a `*`, argument must be present.)
 

From 8c54b2c1101b4510e13741cc50f9f54fbb0c1405 Mon Sep 17 00:00:00 2001
From: Preston Mason 
Date: Wed, 20 Jun 2012 17:49:05 +0800
Subject: [PATCH 03/13] init: 3.2.1

---
 .gitignore                |   3 +
 README.md                 |  49 ++
 doc/tags                  |   6 +
 doc/vimpress.txt          | 133 ++++++
 markdown_posts_upgrade.py | 273 +++++++++++
 plugin/vimrepress.py      | 937 ++++++++++++++++++++++++++++++++++++++
 plugin/vimrepress.vim     |  94 ++++
 release_vimpress.sh       |  16 +
 syntax/blogsyntax.vim     |  12 +
 9 files changed, 1523 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 README.md
 create mode 100644 doc/tags
 create mode 100644 doc/vimpress.txt
 create mode 100755 markdown_posts_upgrade.py
 create mode 100644 plugin/vimrepress.py
 create mode 100644 plugin/vimrepress.vim
 create mode 100755 release_vimpress.sh
 create mode 100644 syntax/blogsyntax.vim

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d42408e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+.hg
+.svn
+.hg*
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..fb90fe0
--- /dev/null
+++ b/README.md
@@ -0,0 +1,49 @@
+#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 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 \ 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
+
+    [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.
+
+###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. 
+
+
diff --git a/doc/tags b/doc/tags
new file mode 100644
index 0000000..7db52e4
--- /dev/null
+++ b/doc/tags
@@ -0,0 +1,6 @@
+COMMANDS	vimpress.txt	/*COMMANDS*
+CONFIGURE	vimpress.txt	/*CONFIGURE*
+EXAMPLES	vimpress.txt	/*EXAMPLES*
+INSTALL	vimpress.txt	/*INSTALL*
+TIPS	vimpress.txt	/*TIPS*
+vimpress.txt	vimpress.txt	/*vimpress.txt*
diff --git a/doc/vimpress.txt b/doc/vimpress.txt
new file mode 100644
index 0000000..88c6725
--- /dev/null
+++ b/doc/vimpress.txt
@@ -0,0 +1,133 @@
+*vimpress.txt*   Plugin for managing wordpress blog from Vim.
+
+Script:   blog.vim
+Authors:  Adrien Friggeri 
+          Pigeond 
+          * Preston M.[BOYPT] 
+          Justin Sattery 
+          Lenin Lee       
+          * Conner McDaniel 
+    (* Currently developing)
+    Conner's Repository: https://github.com/connermcd/VimRepress
+    Preston's Repository: https://bitbucket.org/pentie/vimrepress
+
+License:  Same terms as Vim itself (see |license|)
+
+============================================================================
+
+*INSTALL*
+
+Download vimpress_x.x.x.zip, extract it in your .vim directory:
+
+  cd ~/.vim
+  unzip /path/to/vimpress_x.x.x.zip
+
+*CONFIGURE*
+
+Create file `~/.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 =
+
+>
+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. 
+
+If you use other distributions (or OSs), refer to your package manager or the python-markdown 
+project page: http://www.freewisdom.org/projects/python-markdown/Installation
+
+*COMMANDS*
+
+Vimpress Commands (parameters in square brackets are optional): 
+>
+    :BlogList [] []
+<   Lists a specified number of blog posts or pages for the current blog
+    starting the with most recent.  In this view, you can press  
+    to open a post for edit, or press  to move a post to trash.
+    The Delete function doesn't actually remove your post, but move to 
+    the trash.
+    [] - either post or page, [Default post].
+    []  - number to display (only for posts, Default 30)
+>
+    :BlogNew []
+<   Creates a new page editing window from the current buffer.
+    [] - either post or page. [Default:post]
+>
+    :BlogSave []
+<   Saves the current editing window.
+    [] - either post or publish. [Default:draft] 
+>
+    :BlogPreview []
+<   If set to local, converts the editing window to HTML and displays it 
+    locally in a file browser. Otherwise, the command is the same as 
+    :BlogSave except that it opens a preview of the post or page on the blog.
+    [] - either local, post, or publish. [Default:local]
+>
+    :BlogOpen 
+<   Opens the specified post. 
+     - link you copied from the browser, or simply the 
+                        numberic post id.
+>
+    :BlogSwitch []
+<   Switches the current working blog to the next in the configuration
+    array or the specified index.
+    []   - index of blog to switch to.
+>
+    :BlogUpload 
+<   Uploads a file to the blog's media library, then the attachment URL
+    will append to the current buffer.
+          - /path/to/file
+>
+    :BlogCode []
+<   Appends a pre tag to the end of the current buffer of a specified
+    code language. (i.e. python)
+     [] - the coding language
+
+*TIPS*
+
+|Categories| When you're writing a new post, and you want to fill the "Cats"
+line in the meta field, press  in INSERT mode, a menu will
+show up with all your categories names in your blog. Completion is supported.
+
+|Recovery| If you have a slow connection to your blog, there's a chance that gvim will got hanged during your posting the article, just wait patiently. If still vim crashed with fail posting your article (never happened here), don't  worried, vim had a inteligent recovery system, reopen vim and strike |:recovery| command, your article should appear. Type |:help recovery| for more info.
+
+
+
+*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
+<
+
diff --git a/markdown_posts_upgrade.py b/markdown_posts_upgrade.py
new file mode 100755
index 0000000..1dd1653
--- /dev/null
+++ b/markdown_posts_upgrade.py
@@ -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 = '%(file)s'
+    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 = "", delete = "")
+    DEFAULT_META = dict(strid = "", title = "", slug = "", 
+                        cats = "", tags = "", editformat = "Markdown", 
+                        edittype = "post", textattach = '')
+    TAG_STRING = ""
+    TAG_RE = re.compile(TAG_STRING % dict(url = '(?P\S+)', file = '(?P\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("' + post_more
+    elif len(page_more) > 0:
+        content += '' + 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)
+
+
diff --git a/plugin/vimrepress.py b/plugin/vimrepress.py
new file mode 100644
index 0000000..3404390
--- /dev/null
+++ b/plugin/vimrepress.py
@@ -0,0 +1,937 @@
+# -*- 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 = '' \
+                     '%(file)s'
+    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="", 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)
+    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']
+                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)
+
+            self.__xmlrpc = config["xmlrpc_obj"]
+
+            # Setting tags and categories for completefunc
+            categories = config.get("categories", None)
+            if categories is None:
+                categories = [i["description"].encode("utf-8")
+                        for i in self.xmlrpc.get_categories()]
+                config["categories"] = 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()
+            confile = os.path.expanduser("~/.vimpressrc")
+            conf_options = ("blog_url", "username", "password")
+
+            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:
+                    # wirte 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', definding 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):
+        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()
+        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_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":
+            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'' + 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"]
+                break
+        else:
+            self.raw_text = content
+
+        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""" Vimpress Local Preview: %(title)s     %(content)s  
+""" % 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  %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=blogsyntax')
+    vim.command('setl completefunc=Completable')
+
+
+@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')
+
+
+#################################################
+# 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"):
+        raise VimPressException("Invalid option: %s " % edit_type)
+    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  %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   %(enter)s :py blog_list_on_key_press('open', '%%s')"
+            % G.LIST_VIEW_KEY_MAP % edit_type)
+    vim.command("map   %(delete)s :py blog_list_on_key_press('delete', '%%s')"
+            % G.LIST_VIEW_KEY_MAP % edit_type)
+    echomsg("Press  to edit.  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 = \
+"""
+
""" + 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: + + 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) + + 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) + + # Uesr input something not a usabe url, try numberic + 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)) diff --git a/plugin/vimrepress.vim b/plugin/vimrepress.vim new file mode 100644 index 0000000..c37634e --- /dev/null +++ b/plugin/vimrepress.vim @@ -0,0 +1,94 @@ +"####################################################################### +" 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 +" Pigeond +" Justin Sattery +" Lenin Lee +" Conner McDaniel +" +" Forked By: Preston M.[BOYPT] +" 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 + +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 + +let s:py_loaded = 0 +let s:vimpress_dir = fnamemodify(expand(""), ":p:h") + +function! 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,CompEditType BlogList call PyCMD('blog_list()') +command! -nargs=? -complete=custom,CompEditType BlogNew call PyCMD('blog_new()') +command! -nargs=? -complete=custom,CompSave BlogSave call PyCMD('blog_save()') +command! -nargs=? -complete=custom,CompPrev BlogPreview call PyCMD('blog_preview()') +command! -nargs=1 -complete=file BlogUpload call PyCMD('blog_upload_media()') +command! -nargs=1 BlogOpen call PyCMD('blog_guess_open()') +command! -nargs=? BlogSwitch call PyCMD('blog_config_switch()') +command! -nargs=? BlogCode call PyCMD('blog_append_code()') + diff --git a/release_vimpress.sh b/release_vimpress.sh new file mode 100755 index 0000000..3aaf837 --- /dev/null +++ b/release_vimpress.sh @@ -0,0 +1,16 @@ +#!/bin/bash + + +TEMP_DIR=/tmp/vimpress_relase +REV=`hg summary|grep -e "^parent"|awk '{print $2}'|tr ':' '_'` +BRANCH=`hg branch` +hg archive $TEMP_DIR +VER=`grep Version plugin/vimrepress.vim | awk '{print $3}'` +RELEASE_FILE="/tmp/"$BRANCH"_"$VER"_r"$REV".zip" +cd $TEMP_DIR +if [[ -f $RELEASE_FILE ]]; then rm $RELEASE_FILE; fi +zip -x 'README.md' -x '.hgtags' -x '.hg_archival.txt' -x markdown_posts_upgrade.py -x release_vimpress.sh -r $RELEASE_FILE . +rm -rf $TEMP_DIR +echo $RELEASE_FILE + + diff --git a/syntax/blogsyntax.vim b/syntax/blogsyntax.vim new file mode 100644 index 0000000..130bef5 --- /dev/null +++ b/syntax/blogsyntax.vim @@ -0,0 +1,12 @@ +if version < 600 + syntax clear +elseif exists("b:current_syntax") + finish +endif +sy match blogeditorEntry "^ *[0-9]*\t.*$" +sy match blogeditorComment '^".*$' +sy match blogeditorIdent '^"[^:]*:' +hi link blogeditorComment Comment +hi link blogeditorEntry Directory +hi link blogeditorIdent Function +let b:current_syntax = "blogsyntax" From 4c09ed5136e3a808274afb9d47f5140e86069e00 Mon Sep 17 00:00:00 2001 From: Preston Mason Date: Wed, 20 Jun 2012 17:58:09 +0800 Subject: [PATCH 04/13] update readme --- README.md | 9 --------- 1 file changed, 9 deletions(-) diff --git a/README.md b/README.md index 80d8aa8..fb90fe0 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,6 @@ #Welcome VimRepress is a plugin for managing wordpress blog from Vim, using Markdown syntax. -<<<<<<< HEAD -======= -This is the release mirror repo of: https://bitbucket.org/pentie/vimrepress - ->>>>>>> 0fea160c11f724cc2b866b78d841b8dd2d32e9d2 ##Features * NEW/EDIT/DELETE wordpress Posts/Pages. * In both Markdown / HTML format. @@ -47,12 +42,8 @@ Hardcoding the password is optional. If a password is not provided the plugin wi ###For Upgraded Users -<<<<<<< HEAD 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. -======= -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`. ->>>>>>> 0fea160c11f724cc2b866b78d841b8dd2d32e9d2 From 80bec76d2ee2bb49b56de56a79efece4a70924fb Mon Sep 17 00:00:00 2001 From: Preston Date: Wed, 20 Jun 2012 20:25:08 +0800 Subject: [PATCH 05/13] fix: User completeable func missing by fault --- .gitignore | 1 + doc/tags | 6 ------ plugin/vimrepress.vim | 21 +++++++++++++++++++++ 3 files changed, 22 insertions(+), 6 deletions(-) delete mode 100644 doc/tags diff --git a/.gitignore b/.gitignore index d42408e..6bece32 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +doc/tags .hg .svn .hg* diff --git a/doc/tags b/doc/tags deleted file mode 100644 index 7db52e4..0000000 --- a/doc/tags +++ /dev/null @@ -1,6 +0,0 @@ -COMMANDS vimpress.txt /*COMMANDS* -CONFIGURE vimpress.txt /*CONFIGURE* -EXAMPLES vimpress.txt /*EXAMPLES* -INSTALL vimpress.txt /*INSTALL* -TIPS vimpress.txt /*TIPS* -vimpress.txt vimpress.txt /*vimpress.txt* diff --git a/plugin/vimrepress.vim b/plugin/vimrepress.vim index c37634e..53de6cb 100644 --- a/plugin/vimrepress.vim +++ b/plugin/vimrepress.vim @@ -69,6 +69,27 @@ function! CompEditType(ArgLead, CmdLine, CursorPos) return "post\npage\n" endfunction +function! 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 + let s:py_loaded = 0 let s:vimpress_dir = fnamemodify(expand(""), ":p:h") From 75be3af362ffe4c16c7ecd0d475e6b7dd4912c4c Mon Sep 17 00:00:00 2001 From: Preston Date: Wed, 20 Jun 2012 21:49:01 +0800 Subject: [PATCH 06/13] fix: Image HTML breaks --- plugin/vimrepress.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/vimrepress.py b/plugin/vimrepress.py index 3404390..c20f99d 100644 --- a/plugin/vimrepress.py +++ b/plugin/vimrepress.py @@ -61,7 +61,7 @@ class DataObject(object): DEFAULT_LIST_COUNT = "15" IMAGE_TEMPLATE = '' \ '%(file)s' + ' class="aligncenter" />' MARKER = dict(bg="=========== Meta ============", mid="=============================", ed="========== Content ==========", From a2a3d026d656df03f1424c431ad9335c997838c0 Mon Sep 17 00:00:00 2001 From: Preston Mason Date: Thu, 21 Jun 2012 17:48:16 +0800 Subject: [PATCH 07/13] Add: create new category in BlogNew command. --- plugin/vimrepress.py | 43 +++++++++++++++++++++++++++++++------------ plugin/vimrepress.vim | 37 ++++++++++++++++++++----------------- 2 files changed, 51 insertions(+), 29 deletions(-) diff --git a/plugin/vimrepress.py b/plugin/vimrepress.py index c20f99d..f858d6d 100644 --- a/plugin/vimrepress.py +++ b/plugin/vimrepress.py @@ -156,12 +156,9 @@ def xmlrpc(self): self.__xmlrpc = config["xmlrpc_obj"] # Setting tags and categories for completefunc - categories = config.get("categories", None) - if categories is None: - categories = [i["description"].encode("utf-8") - for i in self.xmlrpc.get_categories()] - config["categories"] = categories + 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 @@ -284,6 +281,9 @@ def get_recent_post_titles(self, retrive_count=0): 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) @@ -613,9 +613,9 @@ def blog_wise_open_view(): @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("let s:user_input = %s('%s :')" % (("inputsecret" if secret else "input"), message)) vim.command('call inputrestore()') - return vim.eval('user_input') + return vim.eval('s:user_input') ################################################# @@ -657,12 +657,29 @@ 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"): + if edit_type.lower() not in ("post", "page", "category"): raise VimPressException("Invalid option: %s " % edit_type) - blog_wise_open_view() - g_data.current_post = ContentStruct(edit_type = edit_type) - cp = g_data.current_post - cp.fill_buffer() + + 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") @@ -935,3 +952,5 @@ def blog_config_switch(index = -1, refresh_list = False): if refresh_list: blog_list(keep_type = True) echomsg("Vimpress switched to '%s'@'%s'" % (g_data.blog_username, g_data.blog_url)) + + diff --git a/plugin/vimrepress.vim b/plugin/vimrepress.vim index 53de6cb..83c4bb0 100644 --- a/plugin/vimrepress.vim +++ b/plugin/vimrepress.vim @@ -57,19 +57,26 @@ if !has("python") finish endif -function! CompSave(ArgLead, CmdLine, CursorPos) +let s:py_loaded = 0 +let s:vimpress_dir = fnamemodify(expand(""), ":p:h") + +function! s:CompSave(ArgLead, CmdLine, CursorPos) return "publish\ndraft\n" endfunction -function! CompPrev(ArgLead, CmdLine, CursorPos) +function! s:CompPrev(ArgLead, CmdLine, CursorPos) return "local\npublish\ndraft\n" endfunction -function! CompEditType(ArgLead, CmdLine, CursorPos) +function! s:CompEditType(ArgLead, CmdLine, CursorPos) return "post\npage\n" endfunction -function! Completable(findstart, base) +function! s:CompNewType(ArgLead, CmdLine, CursorPos) + return "post\npage\ncategory\n" +endfunction + +function! s:Completable(findstart, base) if a:findstart " locate the start of the word let line = getline('.') @@ -90,10 +97,7 @@ function! Completable(findstart, base) endif endfun -let s:py_loaded = 0 -let s:vimpress_dir = fnamemodify(expand(""), ":p:h") - -function! PyCMD(pyfunc) +function! s:PyCMD(pyfunc) if (s:py_loaded == 0) exec("cd " . s:vimpress_dir) let s:pyfile = fnamemodify("vimrepress.py", ":p") @@ -104,12 +108,11 @@ function! PyCMD(pyfunc) exec('python ' . a:pyfunc) endfunction -command! -nargs=? -complete=custom,CompEditType BlogList call PyCMD('blog_list()') -command! -nargs=? -complete=custom,CompEditType BlogNew call PyCMD('blog_new()') -command! -nargs=? -complete=custom,CompSave BlogSave call PyCMD('blog_save()') -command! -nargs=? -complete=custom,CompPrev BlogPreview call PyCMD('blog_preview()') -command! -nargs=1 -complete=file BlogUpload call PyCMD('blog_upload_media()') -command! -nargs=1 BlogOpen call PyCMD('blog_guess_open()') -command! -nargs=? BlogSwitch call PyCMD('blog_config_switch()') -command! -nargs=? BlogCode call PyCMD('blog_append_code()') - +command! -nargs=? -complete=custom,s:CompEditType BlogList call s:PyCMD('blog_list()') +command! -nargs=? -complete=custom,s:CompNewType BlogNew call s:PyCMD('blog_new()') +command! -nargs=? -complete=custom,s:CompSave BlogSave call s:PyCMD('blog_save()') +command! -nargs=? -complete=custom,s:CompPrev BlogPreview call s:PyCMD('blog_preview()') +command! -nargs=1 -complete=file BlogUpload call s:PyCMD('blog_upload_media()') +command! -nargs=1 BlogOpen call s:PyCMD('blog_guess_open()') +command! -nargs=? BlogSwitch call s:PyCMD('blog_config_switch()') +command! -nargs=? BlogCode call s:PyCMD('blog_append_code()') From a3b2e8248e4e86f0a0d5fd89a491c470495c703c Mon Sep 17 00:00:00 2001 From: Preston Mason Date: Tue, 26 Jun 2012 10:28:18 +0800 Subject: [PATCH 08/13] fix: complete func name --- plugin/vimrepress.py | 2 +- plugin/vimrepress.vim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/vimrepress.py b/plugin/vimrepress.py index f858d6d..9b704ec 100644 --- a/plugin/vimrepress.py +++ b/plugin/vimrepress.py @@ -607,7 +607,7 @@ def blog_wise_open_view(): else: vim.command(":new") vim.command('setl syntax=blogsyntax') - vim.command('setl completefunc=Completable') + vim.command('setl completefunc=vimrepress#CateComplete') @vim_encoding_check diff --git a/plugin/vimrepress.vim b/plugin/vimrepress.vim index 83c4bb0..c5a2a18 100644 --- a/plugin/vimrepress.vim +++ b/plugin/vimrepress.vim @@ -76,7 +76,7 @@ function! s:CompNewType(ArgLead, CmdLine, CursorPos) return "post\npage\ncategory\n" endfunction -function! s:Completable(findstart, base) +function! vimrepress#CateComplete(findstart, base) if a:findstart " locate the start of the word let line = getline('.') From fd2a5eb102648296cb1f750e492667674185e701 Mon Sep 17 00:00:00 2001 From: Lorenzo Bolla Date: Tue, 21 Aug 2012 16:22:17 +0100 Subject: [PATCH 09/13] Do not use VIMPRESS in BlogOpen. BlogOpen failed because it was trying to use VIMPRESS. Instead, use `blog_url` as everywhere else. --- plugin/vimrepress.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugin/vimrepress.py b/plugin/vimrepress.py index 9b704ec..2d44a8c 100644 --- a/plugin/vimrepress.py +++ b/plugin/vimrepress.py @@ -896,9 +896,8 @@ def blog_guess_open(what): 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 + if what.startswith(g_data.blog_url): + blog_index = 1 # User input a url contained in the profiles if blog_index != -1: From ae8575f845c61e1eefdaf2df3babb9aaf4c91f64 Mon Sep 17 00:00:00 2001 From: Sudar Date: Mon, 22 Apr 2013 22:18:12 +0530 Subject: [PATCH 10/13] Fixed typos in comments --- plugin/vimrepress.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/vimrepress.py b/plugin/vimrepress.py index 2d44a8c..1eab25b 100644 --- a/plugin/vimrepress.py +++ b/plugin/vimrepress.py @@ -194,7 +194,7 @@ def config(self): except vim.error: pass else: - # wirte config to `~/.vimpressrc`, + # write config to `~/.vimpressrc`, # coding account in .vimrc is obsolesced. if not os.path.exists(confile) and \ self.__config is not None: @@ -207,7 +207,7 @@ def config(self): confpsr.write(f) echomsg("Your Blog accounts are now copied to " - "`~/.vimpressrc', definding account info " + "`~/.vimpressrc', defining account info " "in `.vimrc` is now obsolesced, and may " "lead to secret leak if you share your " "vim configuration with public. Please " From 109d520bfcbb4e9ddddd8fc65f13de51557c9f12 Mon Sep 17 00:00:00 2001 From: Sudar Date: Mon, 22 Apr 2013 22:25:08 +0530 Subject: [PATCH 11/13] Added an option to choose whether to store Markdown text in custom field or not --- README.md | 16 ++++++++++++---- plugin/vimrepress.py | 29 +++++++++++++++++------------ 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index fb90fe0..d12b241 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ #Welcome -VimRepress is a plugin for managing wordpress blog from Vim, using Markdown syntax. +VimRepress is a plugin for managing WordPress blog from Vim, using Markdown syntax. ##Features - * NEW/EDIT/DELETE wordpress Posts/Pages. + * NEW/EDIT/DELETE WordPress Posts/Pages. * In both Markdown / HTML format. - * Markdown text stored in the custom fields of wordpress. + * 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. + * WordPress.com account supported. * Multiple account supported. ##Commands Reference @@ -33,6 +33,12 @@ Create file `~/.vimpressrc` in the following format: 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 @@ -40,6 +46,8 @@ Create file `~/.vimpressrc` in the following format: 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`. diff --git a/plugin/vimrepress.py b/plugin/vimrepress.py index 1eab25b..38ec811 100644 --- a/plugin/vimrepress.py +++ b/plugin/vimrepress.py @@ -82,6 +82,7 @@ class DataObject(object): 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)) @@ -144,6 +145,7 @@ def xmlrpc(self): 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) @@ -151,7 +153,7 @@ def xmlrpc(self): blog_password = vim_input( "Enter password for %s" % blog_url, True) config["xmlrpc_obj"] = wp_xmlrpc(blog_url, - blog_username, blog_password) + blog_username, blog_password, store_markdown) self.__xmlrpc = config["xmlrpc_obj"] @@ -167,9 +169,9 @@ def xmlrpc(self): def config(self): if self.__config is None or len(self.__config) == 0: - confpsr = SafeConfigParser() + confpsr = SafeConfigParser({'store_markdown': 'y'}) confile = os.path.expanduser("~/.vimpressrc") - conf_options = ("blog_url", "username", "password") + conf_options = ("blog_url", "username", "password", "store_markdown") if os.path.exists(confile): conf_list = [] @@ -224,10 +226,11 @@ def config(self): class wp_xmlrpc(object): - def __init__(self, blog_url, username, password): + 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 @@ -410,14 +413,16 @@ def refresh_from_buffer(self): #Translate markdown and save in custom fields. if meta["editformat"].lower() == "markdown": - 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) + 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: From 448378df72936e92d160e54aab48d01ce70c5cf6 Mon Sep 17 00:00:00 2001 From: Sudar Date: Thu, 25 Apr 2013 13:38:58 +0530 Subject: [PATCH 12/13] Added the ability to do syntax highlighting for html and markdown blog posts --- plugin/vimrepress.py | 4 +++- syntax/{blogsyntax.vim => html_blogsyntax.vim} | 1 + syntax/md_blogsyntax.vim | 13 +++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) rename syntax/{blogsyntax.vim => html_blogsyntax.vim} (93%) create mode 100644 syntax/md_blogsyntax.vim diff --git a/plugin/vimrepress.py b/plugin/vimrepress.py index 38ec811..913a979 100644 --- a/plugin/vimrepress.py +++ b/plugin/vimrepress.py @@ -461,9 +461,11 @@ def refresh_from_wp(self, post_id): 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 @@ -611,7 +613,7 @@ def blog_wise_open_view(): vim.command('setl nomodified') else: vim.command(":new") - vim.command('setl syntax=blogsyntax') + vim.command('setl syntax=md_blogsyntax') vim.command('setl completefunc=vimrepress#CateComplete') diff --git a/syntax/blogsyntax.vim b/syntax/html_blogsyntax.vim similarity index 93% rename from syntax/blogsyntax.vim rename to syntax/html_blogsyntax.vim index 130bef5..1c44e28 100644 --- a/syntax/blogsyntax.vim +++ b/syntax/html_blogsyntax.vim @@ -3,6 +3,7 @@ if version < 600 elseif exists("b:current_syntax") finish endif +runtime! syntax/html.vim sy match blogeditorEntry "^ *[0-9]*\t.*$" sy match blogeditorComment '^".*$' sy match blogeditorIdent '^"[^:]*:' diff --git a/syntax/md_blogsyntax.vim b/syntax/md_blogsyntax.vim new file mode 100644 index 0000000..e9b6c5c --- /dev/null +++ b/syntax/md_blogsyntax.vim @@ -0,0 +1,13 @@ +if version < 600 + syntax clear +elseif exists("b:current_syntax") + finish +endif +runtime! syntax/markdown.vim +sy match blogeditorEntry "^ *[0-9]*\t.*$" +sy match blogeditorComment '^".*$' +sy match blogeditorIdent '^"[^:]*:' +hi link blogeditorComment Comment +hi link blogeditorEntry Directory +hi link blogeditorIdent Function +let b:current_syntax = "blogsyntax" From e80f3bc74f776c7048279af96287fa867579520d Mon Sep 17 00:00:00 2001 From: Sudar Date: Thu, 25 Apr 2013 13:40:17 +0530 Subject: [PATCH 13/13] Fixed typos in comments inside code --- plugin/vimrepress.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/vimrepress.py b/plugin/vimrepress.py index 913a979..dab0a86 100644 --- a/plugin/vimrepress.py +++ b/plugin/vimrepress.py @@ -910,7 +910,7 @@ def blog_guess_open(what): if blog_index != -1: guess_id = re.search(r"\S+?p=(\d+)$", what) - # permantlinks + # permalinks if guess_id is None: # try again for /archives/%post_id% @@ -937,7 +937,7 @@ def blog_guess_open(what): if blog_index != -1 and blog_index != g_data.conf_index: blog_config_switch(blog_index) - # Uesr input something not a usabe url, try numberic + # User input not a url, try numeric else: try: post_id = str(int(what))