1
0
mirror of https://github.com/gryf/.vim.git synced 2025-12-17 11:30:29 +01:00

Removed ACP plugin. Found it annoying after a while. Reorganized rst2blogger plugin. Added documentation for it.

This commit is contained in:
2010-12-07 20:57:23 +01:00
parent b3d266aca3
commit 388bc749e7
15 changed files with 760 additions and 2065 deletions

View File

@@ -1,98 +0,0 @@
" Blogger vim interface.
" Provide some convinient functions for creating preview from the reST file
" and to send articles to blog.
if exists("b:did_rst_plugin")
finish " load only once
else
let b:did_rst_plugin = 1
endif
if !exists("g:blogger_browser")
let g:blogger_browser = 0
endif
if !exists("g:blogger_name")
let g:blogger_name = ""
endif
if !exists("g:blogger_login")
let g:blogger_login= ""
endif
if !exists("g:blogger_pass")
let g:blogger_pass = ""
endif
map <F6> :call <SID>Restify()<cr>
map <F7> :call <SID>Rst2Blogger()<cr>
if !exists('*s:Restify')
python << EOF
#{{{
import os
import sys
import webbrowser
import vim
scriptdir = os.path.dirname(vim.eval('expand("<sfile>")'))
sys.path.insert(0, scriptdir)
try:
from vimblogger.rest import blogPreview, blogArticleString
from vimblogger.blogger import VimBlogger
except ImportError:
print "Plugin blogger cannot be loaded, due to lack of required modules"
#}}}
EOF
" Translate reSt text into html fragment suitable for preview in browser.
fun <SID>Restify()
python << EOF
# {{{
bufcontent = "\n".join(vim.current.buffer)
name = vim.current.buffer.name
name = name[:-4] + ".html"
html = blogPreview(bufcontent)
output_file = open(name, "w")
output_file.write(html)
output_file.close()
if vim.eval("g:blogger_browser"):
webbrowser.open(name)
print "Generated HTML has been opened in browser"
else:
print "Generated HTML has been written to %s" % name
#}}}
EOF
endfun
" Generate headless html, gather title, dates and tags from filed list and
" then send it to blog.
fun <SID>Rst2Blogger()
python << EOF
#{{{
bufcontent = "\n".join(vim.current.buffer)
name = vim.current.buffer.name
html, attrs = blogArticleString(bufcontent)
login = vim.eval("g:blogger_login")
password = vim.eval("g:blogger_pass")
blogname = vim.eval("g:blogger_name")
if not password:
password = vim.eval('inputsecret("Enter your gmail password: ")')
title = 'title' in attrs and attrs['title'] or None
date = 'date' in attrs and attrs['date'] or None
tags = 'tags' in attrs and attrs['tags'] or ""
tags = [tag.strip() for tag in tags.split(',')]
modified = 'modified' in attrs and attrs['modified'] or None
blog = VimBlogger(blogname, login, password)
print blog.create_article(title, html, tags=tags)
#}}}
EOF
endfun
endif

View File

@@ -9,3 +9,7 @@ setlocal formatoptions=tcq "set VIms default
let g:blogger_login="gryf73"
let g:blogger_name="rdobosz"
let g:blogger_browser=1
let g:blogger_stylesheets=["css/widget_css_2_bundle.css", "css/style_custom.css", "css/style_blogger.css"]
map <F6> :PreviewBlogArticle<cr>
map <F7> :SendBlogArticle<cr>

View File

@@ -0,0 +1 @@
# module vimblogger

View File

@@ -0,0 +1,258 @@
# vim: fileencoding=utf8
#
# Blogger interface to make easy way to create/update articles for specified
# blog.
#
# It is assumed one way communication only, so you may create or update an
# article from reST source files. There is no way to recreate article from
# html to reST format.
#
# requirements:
#
# - Vim compiled with +python
# - python 2.x (tested with 2.6)
# - modules
# - gdata (http://code.google.com/p/gdata-python-client)
# - docutils (http://docutils.sourceforge.net)
#
# USE CASES:
# 1. Create new post
#
# use reST template:
# ===8<---
# :Title: Blog post title
# :Date: optional publish date (for example: 2010-11-28 18:47:05),
# default: now()
# :Modified: optional, default: None
# :Tags: comma separated blog tags
#
# .. more
#
# --->8===
#
# All four docinfo are optional, however it is nice to give at least a title
# to the article :)
#
#
#
# which is provided under templates directory or as a
# snipMate shoortcut (see .vim/snippets/rst.snippets)
#
#-----------------------------------------------------------------------------
#
import datetime
import re
import atom
from gdata.blogger.client import BloggerClient, BLOG_POST_URL
from gdata.blogger.data import BlogPost
class VimBlogger(object):
"""
"""
DATE_PATTERN = re.compile(r"^(\d{4}-\d{2}-\d{2})"
"T(\d{2}:\d{2}:\d{2})(\.\d{3})?[+-]"
"(\d{2}:\d{2})$")
DATE_FORMAT = "%Y-%m-%d"
TIME_FORMAT = "%H:%M:%S"
TZ_FORMAT = "%H:%M"
# TODO: dodać usuwanie artykułów (prosta lista, wybieramy art,
# potwierdzamy)
# TODO: Dokumentacja jako vimdoc!
def __init__(self, blogname, login, password):
"""
"""
self.draft = True
self.blog_id = None
self.blog = None
self.client = BloggerClient()
self._authorize(login, password)
self.feed = self.client.get_blogs()
self._set_blog(blogname)
def get_articles(self, maxarticles=0):
"""
Return list of articles
"""
feed = self.client.get_posts(self.blog_id)
posts = []
for index, entry in enumerate(feed.entry):
if maxarticles and index >= maxarticles:
break
posts.append((entry.get_post_id(),
entry.title.text,
self._extract_date(entry.published.text)))
return posts
def create_article(self, html_doc, attrs=None):
"""
Create new article
html_doc is content of the article in HTML format, without headers,
preamble, doctype and body tags.
attrs is a dictionary that should hold title, date and tags.
return BlogPost object
"""
if not attrs:
attrs = {}
title = 'title' in attrs and attrs['title'] or ""
title = atom.data.Title(text=title, type="text")
html_doc = atom.data.Content(text=html_doc, type="html")
new_post = BlogPost(title=title, content=html_doc)
if 'tags' in attrs and attrs['tags']:
for tag in attrs['tags'].split(','):
new_post.add_label(tag.strip())
if 'date' in attrs and attrs['date'] and \
self._check_date(attrs['date']):
new_post.published = atom.data.Published(text=attrs['date'])
if self.draft:
new_post.control = atom.data.Control(\
draft=atom.data.Draft(text='yes'))
return self.client.post(new_post, BLOG_POST_URL % self.blog_id)
def update_article(self, html_doc, attrs):
"""
Update article.
html_doc is content of the article in HTML format, without headers,
preamble, doctype and body tags.
attrs is a dictionary that should hold title, date and tags.
return BlogPost object
"""
if "id" not in attrs:
raise Exception("Post Id not found in attributes!")
post = self._get_post(attrs['id'])
post.content = atom.data.Content(text=html_doc, type="html")
# update publish date
if 'date' in attrs and attrs['date'] and \
self._check_date(attrs['date']):
post.published = atom.data.Published(text=attrs['date'])
if 'title' in attrs and attrs['title']:
post.title = atom.data.Title(text=attrs['title'], type="text")
#
# update tag list
if 'tags' in attrs:
tags = [tag.strip() for tag in attrs['tags'].split(',')]
for index, label in enumerate(post.category):
if label.term not in tags:
del(post.category[index])
for tag in tags:
self._add_tag(post, tag.strip())
return self.client.update(post)
def delete_article(self, post_id):
"""
Delete selected article
"""
if not post_id:
return "No article id provided"
post = self._get_post(post_id)
self.client.delete(post)
return None
def _get_post(self, post_id):
"""
"""
post_href = self.blog.get_post_link().href
return self.client.get_feed(post_href + "/%s" % post_id,
desired_class=BlogPost)
def _add_tag(self, post, tag):
"""
post - BlogPost object
tag - string with tag/label to add
"""
for label in post.category:
if label.term == tag:
return
post.add_label(tag)
def _extract_date(self, date_string, time=False):
"""
Extract date from the string and optionally time
"""
if not self.DATE_PATTERN.match(date_string):
return False
if not time:
return self.DATE_PATTERN.match(date_string).groups()[0]
groups = self.DATE_PATTERN.match(date_string).groups()
return groups[0] + " " + groups[1]
def _check_date(self, date):
"""
Parse date as RFC 3339 format, for example:
2010-11-30T21:06:48.678+01:00
or
2010-11-30T21:06:48+01:00
Returns true, if date is acceptable, false otherwise
"""
if not self.DATE_PATTERN.match(date):
return False
groups = self.DATE_PATTERN.match(date).groups()
_date = groups[0]
_time = groups[1]
_tz = len(groups) == 3 and groups[2] or groups[3]
try:
datetime.datetime.strptime(_date, self.DATE_FORMAT)
datetime.datetime.strptime(_time, self.TIME_FORMAT)
datetime.datetime.strptime(_tz, self.TZ_FORMAT)
except ValueError:
return False
return True
def _update_date(self, post, attrs):
"""
Update articles published date
"""
def _authorize(self, login, password):
"""
Try to authorize in Google service.
Authorization is kept in client object. In case of wrong credentials,
exception is thrown.
"""
source = 'Blogger_Python_Sample-2.0'
service = 'blogger'
self.client.client_login(login,
password,
source=source,
service=service)
def _set_blog(self, blogname):
"""
Set correct blog, as defined in blogname
"""
for blog in self.feed.entry:
if blog.get_blog_name() == blogname:
self.blog_id = blog.get_blog_id()
self.blog = blog
break

View File

@@ -0,0 +1,197 @@
# vim: fileencoding=utf8
import webbrowser
from xml.dom import minidom
from xml.parsers.expat import ExpatError
import vim
from rst2blogger.rest import blogPreview, blogArticleString
from rst2blogger.blogger import VimBlogger
class Rst2Blogger(object):
"""
Provide convenient way to communicate between vim and blogger through reST
"""
def __init__(self):
vim.command('call setqflist([])')
self.buff = vim.current.buffer
self.docinfo_len = 0
self._set_docinfo_len()
self.login = vim.eval("g:blogger_login")
self.password = vim.eval("g:blogger_pass")
self.blogname = vim.eval("g:blogger_name")
self.buffer_encoding = vim.eval("&fileencoding")
self.vim_encoding = vim.eval("&encoding")
self.draft = int(vim.eval("g:blogger_draft"))
self.maxarticles = int(vim.eval("g:blogger_maxarticles"))
self.confirm_del = int(vim.eval("g:blogger_confirm_del"))
self.stylesheets = vim.eval("g:blogger_stylesheets")
def preview(self):
"""
Generate HTML Blogger article preview and (optionally) display it in
systems' web browser
"""
bufcontent = "\n".join(self.buff)
name = vim.current.buffer.name
name = name[:-4] + ".html"
html = blogPreview(bufcontent, self.stylesheets)
self._open_qf(self._check_html(html))
output_file = open(name, "w")
output_file.write(html)
output_file.close()
if vim.eval("g:blogger_browser"):
webbrowser.open(name)
return "Generated HTML has been opened in browser"
else:
return "Generated HTML has been written to %s" % name
def post(self):
bufcontent = "\n".join(vim.current.buffer)
html, attrs = blogArticleString(bufcontent)
parse_msg = self._check_html(html, True)
if parse_msg:
self._open_qf(parse_msg)
return "There are errors in generated document"
if not self.password:
self.password = vim.eval('inputsecret("Enter your gmail password: ")')
blog = VimBlogger(self.blogname, self.login, self.password)
blog.draft = self.draft > 0
if 'id' in attrs and attrs['id']:
post = blog.update_article(html, attrs=attrs)
msg = unicode("Article '%s' has been updated" % post.title.text)
msg = msg.encode(self.vim_encoding)
else:
post = blog.create_article(html, attrs=attrs)
msg = "New article with id %s has been created" % \
post.get_post_id()
if not post:
return "There is something fishy with creating new article."
for item, value in (('id', post.get_post_id()),
('date', post.published.text),
('title', post.title.text),
('modified', post.updated.text),
('tags',
", ".join([cat.term for cat in post.category]))):
self._update_docinfo(item, value)
return msg
def delete(self):
"""
Get list of articles, display it to the user, make him choose one and
delete
"""
if not self.password:
self.password = vim.eval('inputsecret("Enter your gmail password: ")')
blog = VimBlogger(self.blogname, self.login, self.password)
posts = blog.get_articles(self.maxarticles)
msg = u"inputlist(["
for index, entries in enumerate(posts):
line = "%2d %s %s" % (index+1,
entries[1],
entries[2])
msg += u'"' + line.replace('"', '\\"') + u'",'
msg = msg[:-1]
msg += u"])"
msg = unicode(msg).encode(self.vim_encoding)
choice = int(vim.eval(msg))
if choice:
art = posts[choice-1]
msg = 'confirm("You are about to delete article \'%s\'. Are you sure?"'
msg = unicode(msg % art[1]).encode(self.vim_encoding)
msg += ', "&No\n&Yes")'
if self.confirm_del:
choice = int(vim.eval(msg))
else:
choice = 2
if choice == 2:
result = blog.delete_article(art[0])
if result is None:
return "Article deleted"
else:
return result
return "No articles deleted"
def _update_docinfo(self, attr, val):
"""
Update current buffer with attributes value
"""
val = unicode(":%s: %s" % (attr.capitalize(), val))
val = val.encode(self.buffer_encoding)
if not self.docinfo_len:
self.buff.append(val, 0)
return
for num, line in enumerate(self.buff[:self.docinfo_len]):
if ':%s:' % attr in line.lower() and line.startswith(":"):
self.buff[num] = val
return
self.buff.append(val, 0)
self.docinfo_len += 1
def _set_docinfo_len(self):
"""
Set docinfo_len, which means number of lines from the beginning of the
buffer to the first empty line.
"""
for num, line in enumerate(self.buff):
if line and line.startswith(':'):
continue
elif not line:
self.docinfo_len = num
break
else:
self.docinfo_len = 0
break
def _open_qf(self, msg):
"""
Open VIm QuickFix window with message, if argument msg is non empty
string.
"""
if msg:
msg1 = "There are problems reported by XML parser:"
msg2 = "Check generated html for errors."
vim.command('call setqflist([{"text": "%s"}, {"text": "%s"}, '
'{"text": "%s"}])' % (msg1, msg, msg2))
vim.command('copen')
def _check_html(self, html, add_container=False):
"""
Check HTML generated document, by simply use minidom parser
If add_container is set to True, entire document is wrapped inside
additional div
returns empty string if parses succeed, else exception message.
"""
if add_container:
html = "<div>" + html + "</div>"
message = ""
try:
minidom.parseString(html)
except ExpatError as ex:
message = str(ex)
return message

View File

@@ -14,7 +14,7 @@ class Attrs(object):
class Pygments(Directive):
"""
Source code syntax hightlighting.
Source code syntax highlighting.
"""
required_arguments = 1
optional_arguments = 0
@@ -72,7 +72,7 @@ class CustomHTMLTranslator(HTMLTranslator):
def depart_docinfo(self, node):
"""
Reset body, remove unnecesairy content.
Reset body, remove unnecessary content.
"""
self.body = []
@@ -165,8 +165,7 @@ class NoHeaderHTMLTranslator(CustomHTMLTranslator):
Harvest docinfo fields and store it in global dictionary.
"""
key, val = [n.astext() for n in node]
key = key.lower()
Attrs.ATTRS[key] = val
Attrs.ATTRS[key.lower()] = val.strip()
def visit_date(self, node):
"""
@@ -176,8 +175,9 @@ class NoHeaderHTMLTranslator(CustomHTMLTranslator):
class PreviewHTMLTranslator(CustomHTMLTranslator):
"""
Class for dislpay article in the browser as a preview.
Class for display article in the browser as a preview.
"""
CSS = []
def __init__(self, document):
"""
Alter levels for the heading tags, define custom, blog specific
@@ -188,9 +188,7 @@ class PreviewHTMLTranslator(CustomHTMLTranslator):
self.initial_header_level = 1
self.section_level = 1
# order of css files is important
self.default_stylesheets = ["css/widget_css_2_bundle.css",
"css/style_custom.css",
"css/style_blogger.css"]
self.default_stylesheets = PreviewHTMLTranslator.CSS
self.stylesheet = [self.stylesheet_link % self.encode(css) \
for css in self.default_stylesheets]
self.body_ = []
@@ -230,21 +228,27 @@ class BlogPreviewWriter(Writer):
"""
Custom Writer class for generating full HTML of the article
"""
def __init__(self):
def __init__(self, stylesheets=None):
Writer.__init__(self)
if not stylesheets:
stylesheets = []
self.translator_class = PreviewHTMLTranslator
self.translator_class.CSS = stylesheets
def translate(self):
self.document.settings.output_encoding = "utf-8"
Writer.translate(self)
def blogPreview(string):
def blogPreview(string, stylesheets=None):
"""
Returns partial HTML of the article, and attribute dictionary
Returns full HTML of the article.
string argument is an article in reST
"""
html_output = core.publish_string(string, writer=BlogPreviewWriter())
if not stylesheets:
stylesheets = []
html_output = core.publish_string(string,
writer=BlogPreviewWriter(stylesheets))
html_output = html_output.strip()
html_output = html_output.replace("<!-- more -->", "\n<!-- more -->\n")
return html_output
@@ -259,5 +263,10 @@ def blogArticleString(string):
html_output = core.publish_string(string, writer=BlogBodyWriter())
html_output = html_output.strip()
html_output = html_output.replace("<!-- more -->", "\n<!-- more -->\n")
return html_output, Attrs.ATTRS
attrs = {}
for key in Attrs.ATTRS:
if Attrs.ATTRS[key]:
attrs[key] = Attrs.ATTRS[key]
return html_output, attrs

View File

@@ -1,130 +0,0 @@
# vim: fileencoding=utf8
#
# Blogger interface to make easy way to create/update articles for specified
# blog.
#
# It is assumed one way communication only, so you may create or update an
# article from reST source files. There is no way to recreate article from
# html to reST format.
#
# requirements:
#
# - Vim compiled with +python
# - python 2.x (tested with 2.6)
# - modules
# - gdata (http://code.google.com/p/gdata-python-client)
# - docutils (http://docutils.sourceforge.net)
# - pytz (http://pytz.sourceforge.net)
#
# USE CASES:
# 1. Create new post
#
# use reST template:
# ===8<---
# :Title: Blog post title
# :Date: optional publish date (for example: 2010-11-28 18:47:05),
# default: now()
# :Modified: optional, default: None
# :Tags: comma separated blog tags
#
# .. more
#
# --->8===
#
# All four docinfo are optional, however it is nice to give at least a title
# to the article :)
#
#
#
# which is provided under templates directory or as a
# snipMate shoortcut (see .vim/snippets/rst.snippets)
#
# vim.eval('inputsecret("Password: ")')
# echomsg expand("%:p")
#-----------------------------------------------------------------------------
#
import getpass # TODO: remove
import time
import datetime
import pytz
import atom
from gdata.blogger.client import BloggerClient
class VimBlogger(object):
"""
"""
def __init__(self, blogname, login, password):
"""
"""
self.blog = None
self.client = BloggerClient()
self._authorize(login, password)
self.feed = self.client.get_blogs()
self._set_blog(blogname)
#self._get_arts(blogname)
def _set_blog(self, blogname):
"""
"""
for blog in self.feed.entry:
if blog.get_blog_name() == blogname:
self.blog = blog
break
def _get_arts(self, blogname):
"""
"""
feed = self.client.get_posts(self.blog.get_blog_id())
for entry in feed.entry:
print entry.title.text
#
import ipdb; ipdb.set_trace()
#
# entry.content obiekt zawiera ciało artykułu (entry.content.text
# posiada czystą formę która mnie interesuje najbardziej, do której
# można pisać
#
# entry.category - lista wszystkich kategorii (blogowych tagów), które
# post posiada. Są to elementy klasy atom.data.Category, które
# łatwiutko stworzyć i dodać do posta:
# import atom
# cat1 = atom.data.Category()
# cat1.term = "nowy tag dla bloggera"
# entry.category.append(cat1)
#
# entry.title przechowuje tytuł posta
def _authorize(self, login, password):
"""
"""
source = 'Blogger_Python_Sample-2.0'
service = 'blogger'
self.client.client_login(login,
password,
source=source,
service=service)
def create_article(self, title, html_doc, tags=None):
"""
"""
blog_id = self.blog.get_blog_id()
if tags is None:
tags = []
return self.client.add_post(blog_id, title, html_doc, labels=tags,
draft=True)
def update_article(self, title, html_doc, tags=None):
"""
"""
pass
if __name__ == "__main__":
p = getpass.getpass("Password: ")
b = VimBlogger("rdobosz", "gryf73@gmail.com", p)

View File

@@ -0,0 +1,72 @@
" reST to blogger vim interface.
" Provide some convinient commands for creating preview from the reST file
" and to send articles to blog.
if exists("b:did_rst_plugin")
finish " load only once
else
let b:did_blogger_plugin = 1
endif
if exists(':PreviewBlogArticle')
finish
endif
if !exists("g:blogger_browser")
let g:blogger_browser = 0
endif
if !exists("g:blogger_name")
let g:blogger_name = ""
endif
if !exists("g:blogger_login")
let g:blogger_login= ""
endif
if !exists("g:blogger_pass")
let g:blogger_pass = "Kurcz4czek"
endif
if !exists("g:blogger_draft")
let g:blogger_draft = 1
endif
if !exists("g:blogger_maxarticles")
let g:blogger_maxarticles = 0
endif
if !exists("g:blogger_confirm_del")
let g:blogger_confirm_del = 1
endif
if !exists("g:blogger_stylesheets")
let g:blogger_stylesheets = []
endif
python << EOF
import os
import sys
import vim
scriptdir = os.path.dirname(vim.eval('expand("<sfile>")'))
sys.path.insert(0, scriptdir)
try:
from rst2blogger.main import Rst2Blogger
except ImportError:
print "Plugin vimblogger cannot be loaded, due to lack of required modules"
EOF
if !exists(":PreviewBlogArticle")
command PreviewBlogArticle py print Rst2Blogger().preview()
endif
if !exists(":SendBlogArticle")
command SendBlogArticle py print Rst2Blogger().post()
endif
if !exists(":DeleteBlogArticle")
command DeleteBlogArticle py print Rst2Blogger().delete()
endif