mirror of
https://github.com/gryf/.vim.git
synced 2025-12-17 19:40:29 +01:00
Removing not longer needed vimblogger plugin
This commit is contained in:
@@ -1,220 +0,0 @@
|
|||||||
:Author: Roman Dobosz, gryf73 at gmail com
|
|
||||||
|
|
||||||
=============
|
|
||||||
vimblogger_ft
|
|
||||||
=============
|
|
||||||
|
|
||||||
vimblogger_ft is a simple reStructuredText_ to Blogger interface through VIm_.
|
|
||||||
|
|
||||||
As the name suggest it is a filetype plugin, which helps to create blog
|
|
||||||
articles in rsST format and send them to blog site. It also provides commands
|
|
||||||
for preview in browser and delete articles.
|
|
||||||
|
|
||||||
Requirements
|
|
||||||
------------
|
|
||||||
|
|
||||||
Module for communication was written in Python. So, VIm has to be
|
|
||||||
compiled with ``+python``.
|
|
||||||
|
|
||||||
Other requirements:
|
|
||||||
|
|
||||||
- Python (tested with version 2.6, should work also in others)
|
|
||||||
|
|
||||||
- gdata_
|
|
||||||
- docutils_
|
|
||||||
- Pygments_ (optional)
|
|
||||||
|
|
||||||
- Blogger account
|
|
||||||
|
|
||||||
Install
|
|
||||||
-------
|
|
||||||
|
|
||||||
Download_, edit the vba with VIm and type::
|
|
||||||
|
|
||||||
:so %
|
|
||||||
|
|
||||||
Or, clone this repository and put files in your ``~/.vim`` directory.
|
|
||||||
|
|
||||||
Usage
|
|
||||||
-----
|
|
||||||
|
|
||||||
This plugin is targeting for people, who has blogger account, want to
|
|
||||||
use VIm for creating blog articles and don't really likes to manually do
|
|
||||||
this in html.
|
|
||||||
|
|
||||||
Before starting writing a post, at least ``g:blogger_name`` and
|
|
||||||
``g:blogger_login`` has to be set up in ``.vimrc``. Next, an article has to
|
|
||||||
be written using standard reST markup, ``:Title:`` added (not required,
|
|
||||||
but it's nice to have some title for a blog entry). Now,
|
|
||||||
``:PreviewBlogArticle`` can be used for saving generated HTML page into
|
|
||||||
the file of the same name as reST file. Please note, that it'll silently
|
|
||||||
overwrite existing file, because it is treated as a temporary file.
|
|
||||||
|
|
||||||
When article is done, ``:SendBlogArticle`` will send it to the server.
|
|
||||||
|
|
||||||
Output provided by ``:PreviewBlogArticle`` without any
|
|
||||||
css stylesheet will look pretty raw, so it is generally good idea to
|
|
||||||
grab stylesheets from blog itself, and tweak it a little, and add to
|
|
||||||
list in ``g:blogger_stylesheets``. They will be automatically linked to
|
|
||||||
generated preview file.
|
|
||||||
|
|
||||||
Unfortunately, this script has several limitations, like it is
|
|
||||||
impossible to use multiple blogs or edit existing articles without reST
|
|
||||||
source files. It has to be somehow converted to reStructuredText, id of
|
|
||||||
an article added to ``:Id:`` docinfo item and then updated. Id of an
|
|
||||||
article is available through blogger account - every action for each
|
|
||||||
post listed on Posting->Edit Posts has URL with query string item
|
|
||||||
postID, for example::
|
|
||||||
|
|
||||||
http://www.blogger.com/post-edit.g?blogID=9876&postID=12345
|
|
||||||
|
|
||||||
See plugin documentation for configuration.
|
|
||||||
|
|
||||||
Commands
|
|
||||||
--------
|
|
||||||
|
|
||||||
#. ``:PreviewBlogArticle`` - Generate article in HTML format, save it to the
|
|
||||||
file with te same name as a reST source with .html extension in the same
|
|
||||||
directory, and optionally opens it in browser. No connection to the blogger
|
|
||||||
is performed.
|
|
||||||
#. ``:SendBlogArticle`` -
|
|
||||||
Generate partial HTML document, which holds article, from current
|
|
||||||
reST buffer and send it to the blog.
|
|
||||||
|
|
||||||
See reST document structure below for further description.
|
|
||||||
#. ``:DeleteBlogArticle`` -
|
|
||||||
Display list of articles, and lets user choose one (or none) of them
|
|
||||||
to perform deletions.
|
|
||||||
|
|
||||||
reST document structure
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
It is assumed, that following template will be used::
|
|
||||||
|
|
||||||
:Id:
|
|
||||||
:Title: Title for the blog article
|
|
||||||
:Date:
|
|
||||||
:Modified:
|
|
||||||
:Tags: some, tags
|
|
||||||
|
|
||||||
Penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla
|
|
||||||
facilisis massa ut massa. Sed nisi purus, malesuada eu, porta vulputate,
|
|
||||||
suscipit auctor, nunc. Vestibulum convallis, augue eu luctus malesuada,
|
|
||||||
mi ante mattis odio, ac venenatis neque sem vitae nisi.
|
|
||||||
|
|
||||||
.. more
|
|
||||||
|
|
||||||
heading
|
|
||||||
-------
|
|
||||||
|
|
||||||
**Congue** mi, quis posuere augue nulla a augue. Pellentesque sed est.
|
|
||||||
Mauris cursus urna id lectus. Integer dignissim feugiat eros. Sed tempor
|
|
||||||
volutpat dolor. Vestibulum vel lectus nec mauris semper adipiscing.
|
|
||||||
|
|
||||||
Aliquam tincidunt enim sit amet tellus. Sed mauris nulla, semper
|
|
||||||
tincidunt, luctus a, sodales eget, leo. Sed ligula augue, cursus et.
|
|
||||||
|
|
||||||
reST document (optionally) starts with *docinfo* section (first several
|
|
||||||
lines, that are starting from ":" character) separaded from other
|
|
||||||
content with one empty line.
|
|
||||||
|
|
||||||
Docinfo items holds article attributes, and are updated automatically
|
|
||||||
every each of upload to blogger, which is triggered by
|
|
||||||
":SendBlogArticle" command.
|
|
||||||
|
|
||||||
- **:Id:** - Holds article id on blogger side. If not defined, new article
|
|
||||||
will be created (even if there is already existing one with the very same
|
|
||||||
content). If wrong Id is entered (or an Id of deleted article),
|
|
||||||
exception will be raised, and no action on blogger side will be
|
|
||||||
performed.
|
|
||||||
- **:Title:** - Holds article title. Can be changed when ``:Id:`` is obtained.
|
|
||||||
- **:Date:** - This is published date in RFC 3339
|
|
||||||
http://www.ietf.org/rfc/rfc3339.txt format. If empty on first
|
|
||||||
upload, it will be set to current date. Can be set/changed to
|
|
||||||
desired date.
|
|
||||||
- **:Modified:** - This is read-only item, which store modification date
|
|
||||||
which happens on blogger side.
|
|
||||||
- **:Tags:** - Comma separated list of tags (Labels). Can be empty.
|
|
||||||
|
|
||||||
All other items are ignored.
|
|
||||||
|
|
||||||
After docinfo block, article body should be placed using markup for
|
|
||||||
reStructuredText.
|
|
||||||
|
|
||||||
Note, that ``.. more`` will became HTML comment ``<!-- more -->`` which will
|
|
||||||
prevent from displaying entire post on the bloggers front page, but will
|
|
||||||
not have any visible effect during preview in browser.
|
|
||||||
|
|
||||||
Pygments code highlighting
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
Additionally, if Pygments is installed, there is ``sourcecode`` directive,
|
|
||||||
simple syntax highlighter using Pygments module. Very simple usage for Python
|
|
||||||
code could be as follows::
|
|
||||||
|
|
||||||
.. sourcecode:: python
|
|
||||||
|
|
||||||
import vim
|
|
||||||
print vim.current.buffer.name
|
|
||||||
|
|
||||||
Note, that ``sourcecode`` directive requires argument with the name of the
|
|
||||||
lexer to use. If wrong/non existent lexer is provided, it will fall back to
|
|
||||||
*text* lexer. For more information about available lexers, please refer to
|
|
||||||
Pygments documentation.
|
|
||||||
|
|
||||||
Directive ``sourcecode`` supports two options: ``:linenos:`` and
|
|
||||||
``:cssclass:``.
|
|
||||||
|
|
||||||
``:linenos:`` takes zero or one argument - if no arguments is provided, line
|
|
||||||
numbers will be visible starting form 1. Provided integer will be the number
|
|
||||||
of the first line.
|
|
||||||
|
|
||||||
``:cssclass:`` can be use for changing default class name for block of code.
|
|
||||||
Default class can be changed by appropriate option for plugin (see
|
|
||||||
documentation), and defaults to "highlight".
|
|
||||||
|
|
||||||
It is possible to use VIm colorschemes like desert (which is distributed with
|
|
||||||
VIm), Zenburn_, Lucius_, Wombat_, inkpot_ or any other with Pygments.
|
|
||||||
Assuming, that colorscheme *desert* should be used, there are two steps to
|
|
||||||
achive it.
|
|
||||||
|
|
||||||
First, python module containing Pygments *Style* class has to be generated.
|
|
||||||
There is apropriate convertion tool in Pygments distribution -
|
|
||||||
``scripts/vim2pygments.py``. Uage is simple as::
|
|
||||||
|
|
||||||
python Pygments/scripts/vim2pygments.py [path/to/vim/colors]/desert.vim > desert.py
|
|
||||||
|
|
||||||
Which will create new python module ``desert.py`` containing class
|
|
||||||
``DessertStyle``.
|
|
||||||
|
|
||||||
To generate CSS stylesheet, it's enough to::
|
|
||||||
|
|
||||||
python rst2blogger/scripts/style2css.py desert.py -c VimDesert > desert.css
|
|
||||||
|
|
||||||
VimDesert is the name of the class, which passed as an argument to
|
|
||||||
``:cssclass:`` option of directive ``sourceocode``. It will be used as a main
|
|
||||||
CSS class for code top ``<div>`` element. So, above example will looks like
|
|
||||||
this::
|
|
||||||
|
|
||||||
.. sourcecode:: python
|
|
||||||
:cssclass: VimDesert
|
|
||||||
|
|
||||||
import vim
|
|
||||||
print vim.current.buffer.name
|
|
||||||
|
|
||||||
Note: All headings for generated HTML by ``:SendBlogArticle`` will be
|
|
||||||
shifted by 3, so the first heading will become <h3>, second <h4> and so
|
|
||||||
on, to fit into blogger template (well, most of them). Remember, that
|
|
||||||
HTML allow up to 6 level of headings, while reST doesn't have this
|
|
||||||
limitation.
|
|
||||||
|
|
||||||
.. _VIm: http://www.vim.org
|
|
||||||
.. _gdata: http://code.google.com/p/gdata-python-client
|
|
||||||
.. _docutils: http://docutils.sourceforge.net
|
|
||||||
.. _Pygments: http://pygments.org
|
|
||||||
.. _reStructuredText: http://docutils.sourceforge.net/rst.html
|
|
||||||
.. _Download: http://www.vim.org/scripts/script.php?script_id=3367
|
|
||||||
.. _Zenburn: http://www.vim.org/scripts/script.php?script_id=415
|
|
||||||
.. _inkpot: http://www.vim.org/scripts/script.php?script_id=1143
|
|
||||||
.. _Lucius: http://www.vim.org/scripts/script.php?script_id=2536
|
|
||||||
.. _Wombat: http://www.vim.org/scripts/script.php?script_id=1778
|
|
||||||
@@ -1,282 +0,0 @@
|
|||||||
*vimblogger_ft.txt* reStructuredText to Blogger interface
|
|
||||||
Author: Roman Dobosz, gryf73 at gmail com
|
|
||||||
|
|
||||||
Simple interface to create blog articles in rsST format. It provides
|
|
||||||
commands for preview in browser, post and delete articles.
|
|
||||||
|
|
||||||
-----------------------------------------------------------------------
|
|
||||||
Requirements~
|
|
||||||
|
|
||||||
Module for communication was written in Python. So, VIm has to be
|
|
||||||
compiled with +python.
|
|
||||||
|
|
||||||
Other requirements:
|
|
||||||
|
|
||||||
- Python (tested with version 2.6, should work also in others)
|
|
||||||
- gdata http://code.google.com/p/gdata-python-client
|
|
||||||
- docutils http://docutils.sourceforge.net
|
|
||||||
- Pygments http://pygments.org (optional)
|
|
||||||
- Blogger account
|
|
||||||
|
|
||||||
-----------------------------------------------------------------------
|
|
||||||
Install~
|
|
||||||
|
|
||||||
Edit the vba file and type: >
|
|
||||||
|
|
||||||
:so %
|
|
||||||
|
|
||||||
========================================================================
|
|
||||||
Usage~
|
|
||||||
|
|
||||||
This plugin is targeting for people, who has blogger account, want to
|
|
||||||
use VIm for creating blog articles and don't really likes to manually do
|
|
||||||
this in html.
|
|
||||||
|
|
||||||
Before starting writing a post, at least |g:blogger_name| and
|
|
||||||
|g:blogger_login| has to be set up in |.vimrc|. Next, an article has to
|
|
||||||
be written using standard reST markup, |:Title:| added (not required,
|
|
||||||
but it's nice to have some title for a blog entry). Now,
|
|
||||||
|:PreviewBlogArticle| can be used for saving generated HTML page into
|
|
||||||
the file of the same name as reST file. Please note, that it'll silently
|
|
||||||
overwrite existing file, because it is treated as a temporary file.
|
|
||||||
|
|
||||||
When article is done, |:SendBlogArticle| will send it to the server.
|
|
||||||
|
|
||||||
Output provided by |:PreviewBlogArticle| without any
|
|
||||||
css stylesheet will look pretty raw, so it is generally good idea to
|
|
||||||
grab stylesheets from blog itself, tweak it a little, and add to list
|
|
||||||
in |g:blogger_stylesheets|. They will be automatically linked to
|
|
||||||
generated preview file.
|
|
||||||
|
|
||||||
Unfortunately, this script has several limitations, like it is
|
|
||||||
impossible to use multiple blogs or edit existing articles without reST
|
|
||||||
source files. It has to be somehow converted to reStructuredText, id of
|
|
||||||
an article added to |:Id:| docinfo item and then updated. Id of an
|
|
||||||
article is available through blogger account - every action for each
|
|
||||||
post listed on Posting->Edit Posts has URL with query string item
|
|
||||||
postID, for example:
|
|
||||||
|
|
||||||
>
|
|
||||||
http://www.blogger.com/post-edit.g?blogID=9876&postID=12345
|
|
||||||
<
|
|
||||||
|
|
||||||
-----------------------------------------------------------------------
|
|
||||||
Options~
|
|
||||||
*g:blogger_browser*
|
|
||||||
g:blogger_browser (default: 0)
|
|
||||||
|
|
||||||
If set to 1 output file from :PreviewBlogArticle will be opened in
|
|
||||||
browser (used webbrowser Python module)
|
|
||||||
|
|
||||||
*g:blogger_name*
|
|
||||||
g:blogger_name (default: "")
|
|
||||||
|
|
||||||
This is blog name, which is part of the URL, and user was obligated
|
|
||||||
to enter it during blog creation. If in doubt, check first part od
|
|
||||||
the URL of the blog, just after 'http://'. Note, that blog name may
|
|
||||||
differ from the blog title, but also could be the same.
|
|
||||||
|
|
||||||
*g:blogger_login*
|
|
||||||
g:blogger_login (default: "")
|
|
||||||
|
|
||||||
Google login name, usually gmail address.
|
|
||||||
*g:blogger_pass*
|
|
||||||
g:blogger_pass (default: "")
|
|
||||||
|
|
||||||
Password. If set to empty string, user will be asked for it every
|
|
||||||
time if any blogger connectivity is performed.
|
|
||||||
*g:blogger_draft*
|
|
||||||
g:blogger_draft (default: 1)
|
|
||||||
|
|
||||||
By default, don't publish articles immediately, just save it on the
|
|
||||||
service. If set to 0, article will be published.
|
|
||||||
|
|
||||||
*g:blogger_maxarticles*
|
|
||||||
g:blogger_maxarticles (default: 0)
|
|
||||||
|
|
||||||
Number of displayed articles during deletion. 0 means all. Any
|
|
||||||
positive number will display only that numbers of articles on list.
|
|
||||||
|
|
||||||
*g:blogger_confirm_del*
|
|
||||||
g:blogger_confirm_del (default: 1)
|
|
||||||
|
|
||||||
Confirm every deletion. If set to 0, suppress the confirmation.
|
|
||||||
|
|
||||||
*g:blogger_stylesheets*
|
|
||||||
g:blogger_stylesheets (default: [])
|
|
||||||
|
|
||||||
List of relative paths (relative to generated HTML document) of CSS
|
|
||||||
stylesheets, used only for article preview in HTML document. Usually
|
|
||||||
one wanted to save stylesheets from his own blog, so that article
|
|
||||||
can be displayed almost in the same way as in blog.
|
|
||||||
|
|
||||||
*g:blogger_pygments_class*
|
|
||||||
g:blogger_pygments_class (default: "")
|
|
||||||
|
|
||||||
Name of default CSS class for usage with Pygments. When not
|
|
||||||
provided or empty, it'll use defaults from Pygments: "highlight".
|
|
||||||
|
|
||||||
========================================================================
|
|
||||||
Commands~
|
|
||||||
|
|
||||||
*:PreviewBlogArticle*
|
|
||||||
|
|
||||||
Generate article in HTML format, save it to the file with te same
|
|
||||||
name as a reST source with .html extension in the same directory,
|
|
||||||
and optionally opens it in browser. No connection to the blogger is
|
|
||||||
performed.
|
|
||||||
|
|
||||||
*:SendBlogArticle*
|
|
||||||
|
|
||||||
Generate partial HTML document, which holds article, from current
|
|
||||||
reST buffer and send it to the blog.
|
|
||||||
|
|
||||||
See reST document structure below for further description.
|
|
||||||
|
|
||||||
*:DeleteBlogArticle*
|
|
||||||
|
|
||||||
Display list of articles, and lets user choose one (or none) of them
|
|
||||||
to perform deletions.
|
|
||||||
|
|
||||||
========================================================================
|
|
||||||
reST document structure~
|
|
||||||
|
|
||||||
It is assumed, that following template will be used:
|
|
||||||
>
|
|
||||||
:Id:
|
|
||||||
:Title: Title for the blog article
|
|
||||||
:Date:
|
|
||||||
:Modified:
|
|
||||||
:Tags: some, tags
|
|
||||||
|
|
||||||
Penatibus et magnis dis parturient montes, nascetur ridiculus mus.
|
|
||||||
Nulla facilisis massa ut massa. Sed nisi purus, malesuada eu, porta
|
|
||||||
vulputate, suscipit auctor, nunc. Vestibulum convallis, augue eu luctus
|
|
||||||
malesuada, mi ante mattis odio, ac venenatis neque sem vitae nisi.
|
|
||||||
|
|
||||||
.. more
|
|
||||||
|
|
||||||
|
|
||||||
heading
|
|
||||||
-------
|
|
||||||
|
|
||||||
**Congue** mi, quis posuere augue nulla a augue. Pellentesque sed est.
|
|
||||||
Mauris cursus urna id lectus. Integer dignissim feugiat eros. Sed
|
|
||||||
tempor volutpat dolor. Vestibulum vel lectus nec mauris semper
|
|
||||||
adipiscing.
|
|
||||||
|
|
||||||
Aliquam tincidunt enim sit amet tellus. Sed mauris nulla, semper
|
|
||||||
tincidunt, luctus a, sodales eget, leo. Sed ligula augue, cursus et.
|
|
||||||
<
|
|
||||||
reST document (optionally) starts with *docinfo* section (first several
|
|
||||||
lines, that are starting from ":" character) separaded from other
|
|
||||||
content with one empty line.
|
|
||||||
|
|
||||||
Docinfo items holds article attributes, and are updated automatically
|
|
||||||
every each of upload to blogger, which is triggered by
|
|
||||||
":SendBlogArticle" command.
|
|
||||||
|
|
||||||
*:Id:*
|
|
||||||
Holds article id on blogger side. If not defined, new article will
|
|
||||||
be created (even if there is already existing one with the very same
|
|
||||||
content). If wrong Id is entered (or an Id of deleted article),
|
|
||||||
exception will be raised, and no action on blogger side will be
|
|
||||||
performed.
|
|
||||||
|
|
||||||
*:Title:*
|
|
||||||
Holds article title. Can be changed when |:Id:| is obtained.
|
|
||||||
|
|
||||||
*:Date:*
|
|
||||||
This is published date in RFC 3339
|
|
||||||
http://www.ietf.org/rfc/rfc3339.txt format. If empty on first
|
|
||||||
upload, it will be set to current date. Can be set/changed to
|
|
||||||
desired date.
|
|
||||||
|
|
||||||
*:Modified:*
|
|
||||||
This is read-only item, which store modification date which happens
|
|
||||||
on blogger side.
|
|
||||||
|
|
||||||
*:Tags:*
|
|
||||||
Comma separated list of tags (Labels). Can be empty.
|
|
||||||
|
|
||||||
All other items are ignored.
|
|
||||||
|
|
||||||
After docinfo block, article body should be placed using markup for
|
|
||||||
reStructuredText.
|
|
||||||
|
|
||||||
Note, that `.. more' will became HTML comment `<!-- more -->' which will
|
|
||||||
prevent from displaying entire post on the bloggers front page, but will
|
|
||||||
not have any visible effect during preview in browser.
|
|
||||||
|
|
||||||
========================================================================
|
|
||||||
Pygments code highlighting~
|
|
||||||
|
|
||||||
Additionally, if Pygments is installed, there is ``sourcecode``
|
|
||||||
directive, simple syntax highlighter using Pygments module. Very simple
|
|
||||||
usage for Python code could be as follows:
|
|
||||||
>
|
|
||||||
.. sourcecode:: python
|
|
||||||
|
|
||||||
import vim
|
|
||||||
print vim.current.buffer.name
|
|
||||||
<
|
|
||||||
Note, that `sourcecode' directive requires argument with the name of the
|
|
||||||
lexer to use. If wrong/non existent lexer is provided, it will fall back
|
|
||||||
to `text' lexer. For more information about available lexers, please
|
|
||||||
refer to Pygments documentation.
|
|
||||||
|
|
||||||
Directive `sourcecode' supports two options: `:linenos:' and
|
|
||||||
`:cssclass:'.
|
|
||||||
|
|
||||||
`:linenos:' takes zero or one argument - if no arguments is provided,
|
|
||||||
line numbers will be visible starting form 1. Provided integer will be
|
|
||||||
the number of the first line.
|
|
||||||
|
|
||||||
`:cssclass:' can be use for changing default class name for block of
|
|
||||||
code. Default class can be changed by appropriate option for plugin
|
|
||||||
(see documentation), and defaults to "highlight".
|
|
||||||
|
|
||||||
It is possible to use VIm colorschemes like desert (which is distributed
|
|
||||||
with VIm), Zenburn, Lucius, Wombat, inkpot or any other with Pygments.
|
|
||||||
Assuming, that colorscheme `desert' should be used, there are two steps
|
|
||||||
to achive it.
|
|
||||||
|
|
||||||
First, python module containing Pygments `Style' class has to be
|
|
||||||
generated. There is appropriate conversion tool in Pygments
|
|
||||||
distribution - `scripts/vim2pygments.py'. Uage is simple as:
|
|
||||||
>
|
|
||||||
python Pygments/scripts/vim2pygments.py \
|
|
||||||
path/to/vim/colors/desert.vim > desert.py
|
|
||||||
<
|
|
||||||
Which will create new python module ``desert.py`` containing class
|
|
||||||
`DessertStyle`'.
|
|
||||||
|
|
||||||
To generate CSS stylesheet, it's enough to:
|
|
||||||
>
|
|
||||||
python rst2blogger/scripts/style2css.py desert.py \
|
|
||||||
-c VimDesert > desert.css
|
|
||||||
<
|
|
||||||
VimDesert is the name of the class, which passed as an argument to
|
|
||||||
`:cssclass:' option of directive `sourceocode'. It will be used as a
|
|
||||||
main CSS class for code top `<div>' element. So, above example will
|
|
||||||
looks like this:
|
|
||||||
>
|
|
||||||
.. sourcecode:: python
|
|
||||||
:cssclass: VimDesert
|
|
||||||
|
|
||||||
import vim
|
|
||||||
print vim.current.buffer.name
|
|
||||||
<
|
|
||||||
Note: All headings for generated HTML by |:SendBlogArticle| will be
|
|
||||||
shifted by 3, so the first heading will become <h3>, second <h4> and so
|
|
||||||
on, to fit into blogger template (well, most of them). Remember, that
|
|
||||||
HTML allow up to 6 level of headings, while reST doesn't have this
|
|
||||||
limitation.
|
|
||||||
|
|
||||||
========================================================================
|
|
||||||
Changelog~
|
|
||||||
|
|
||||||
0.2 Pygments sourcecode directive improvements
|
|
||||||
0.1 First release
|
|
||||||
|
|
||||||
vim:tw=72:fo=tcq2:isk=!-~,^*,^|,^":ts=8:ft=help:norl:
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
# module rst2blogger
|
|
||||||
@@ -1,214 +0,0 @@
|
|||||||
"""
|
|
||||||
File: blogger.py
|
|
||||||
Author: Roman 'gryf' Dobosz
|
|
||||||
Description: This is blogger activity connected module. It is using gdata[1]
|
|
||||||
blogger module to provide add/modify/delete articles interface.
|
|
||||||
|
|
||||||
[1] http://code.google.com/p/gdata-python-client
|
|
||||||
"""
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
import re
|
|
||||||
|
|
||||||
import atom
|
|
||||||
from gdata.blogger.client import BloggerClient, BLOG_POST_URL
|
|
||||||
from gdata.blogger.data import BlogPost
|
|
||||||
|
|
||||||
|
|
||||||
class VimBlogger(object):
|
|
||||||
"""
|
|
||||||
Communicate with blogger through gdata.blogger modules
|
|
||||||
"""
|
|
||||||
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"
|
|
||||||
|
|
||||||
def __init__(self, blogname, login, password):
|
|
||||||
"""
|
|
||||||
Initialization.
|
|
||||||
"""
|
|
||||||
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 published 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):
|
|
||||||
"""
|
|
||||||
Return post with specified 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 _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 = 'Vim rst2blogger interface'
|
|
||||||
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
|
|
||||||
@@ -1,214 +0,0 @@
|
|||||||
# vim: fileencoding=utf8
|
|
||||||
"""
|
|
||||||
File: main.py
|
|
||||||
Author: Roman 'gryf' Dobosz
|
|
||||||
Description: main file to provide fuctionality between vim and moudles rest
|
|
||||||
and blogger
|
|
||||||
"""
|
|
||||||
|
|
||||||
import webbrowser
|
|
||||||
from xml.dom import minidom
|
|
||||||
from xml.parsers.expat import ExpatError
|
|
||||||
|
|
||||||
import vim
|
|
||||||
|
|
||||||
from rst2blogger.rest import blogPreview, blogArticleString
|
|
||||||
try:
|
|
||||||
from rst2blogger.rest import register
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
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")
|
|
||||||
self.pygments_class = vim.eval("g:blogger_pygments_class")
|
|
||||||
try:
|
|
||||||
register(self.pygments_class)
|
|
||||||
except NameError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def preview(self):
|
|
||||||
"""
|
|
||||||
Generate HTML Blogger article preview and (optionally) display it in
|
|
||||||
systems' web browser
|
|
||||||
"""
|
|
||||||
bufcontent = "\n".join(self.buff)
|
|
||||||
name = self.buff.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):
|
|
||||||
"""
|
|
||||||
Do post article
|
|
||||||
"""
|
|
||||||
bufcontent = "\n".join(self.buff)
|
|
||||||
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()
|
|
||||||
|
|
||||||
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\'. '
|
|
||||||
msg += '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:
|
|
||||||
blog.delete_article(art[0])
|
|
||||||
return "Article deleted"
|
|
||||||
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.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# minidom doesn't understand html entities like ' ' For checking
|
|
||||||
# purpose it is perfectly ok, to switch them with '&'
|
|
||||||
html = html.replace(" ", "&")
|
|
||||||
if add_container:
|
|
||||||
html = "<div>" + html + "</div>"
|
|
||||||
|
|
||||||
message = ""
|
|
||||||
try:
|
|
||||||
minidom.parseString(html)
|
|
||||||
except ExpatError as ex:
|
|
||||||
message = str(ex)
|
|
||||||
|
|
||||||
return message
|
|
||||||
@@ -1,330 +0,0 @@
|
|||||||
"""
|
|
||||||
File: rest.py
|
|
||||||
Author: Roman 'gryf' Dobosz
|
|
||||||
Description: This module is responsible for conversion between reST and HTML
|
|
||||||
with some goods added.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import re
|
|
||||||
|
|
||||||
from docutils import core
|
|
||||||
from docutils import nodes
|
|
||||||
from docutils.parsers.rst import directives, Directive
|
|
||||||
from docutils.writers.html4css1 import Writer, HTMLTranslator
|
|
||||||
|
|
||||||
try:
|
|
||||||
from pygments import highlight
|
|
||||||
from pygments.lexers import get_lexer_by_name, TextLexer
|
|
||||||
from pygments.formatters import HtmlFormatter
|
|
||||||
|
|
||||||
def register(cssclass=None):
|
|
||||||
if cssclass:
|
|
||||||
Pygments.cssclass = cssclass
|
|
||||||
directives.register_directive('sourcecode', Pygments)
|
|
||||||
|
|
||||||
def _positive_int_or_1(argument):
|
|
||||||
"""
|
|
||||||
Converts the argument into an integer. Returns positive integer. In
|
|
||||||
case of integers smaller than 1, returns 1. In case of None, returns
|
|
||||||
1.
|
|
||||||
"""
|
|
||||||
if argument is None:
|
|
||||||
return 1
|
|
||||||
|
|
||||||
retval = 1
|
|
||||||
try:
|
|
||||||
retval = int(argument)
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if retval < 1:
|
|
||||||
return 1
|
|
||||||
|
|
||||||
return retval
|
|
||||||
|
|
||||||
class Pygments(Directive):
|
|
||||||
"""
|
|
||||||
Source code syntax highlighting.
|
|
||||||
"""
|
|
||||||
required_arguments = 1
|
|
||||||
optional_arguments = 0
|
|
||||||
final_argument_whitespace = True
|
|
||||||
option_spec = {'linenos': _positive_int_or_1,
|
|
||||||
'cssclass': directives.unchanged_required}
|
|
||||||
has_content = True
|
|
||||||
cssclass = None
|
|
||||||
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
self.assert_has_content()
|
|
||||||
try:
|
|
||||||
lexer = get_lexer_by_name(self.arguments[0])
|
|
||||||
except ValueError:
|
|
||||||
# no lexer found - use the text one instead of an exception
|
|
||||||
lexer = TextLexer()
|
|
||||||
|
|
||||||
# take an arbitrary option if more than one is given
|
|
||||||
kwargs = {'full': False,
|
|
||||||
'noclasses': False}
|
|
||||||
|
|
||||||
if self.options and 'linenos' in self.options:
|
|
||||||
kwargs['linenos'] = 'inline'
|
|
||||||
kwargs['linenostart'] = self.options['linenos']
|
|
||||||
|
|
||||||
if Pygments.cssclass:
|
|
||||||
kwargs['cssclass'] = Pygments.cssclass
|
|
||||||
|
|
||||||
if self.options and 'cssclass' in self.options:
|
|
||||||
kwargs['cssclass'] = self.options['cssclass']
|
|
||||||
|
|
||||||
formatter = HtmlFormatter(**kwargs)
|
|
||||||
|
|
||||||
parsed = highlight(u'\n'.join(self.content), lexer, formatter)
|
|
||||||
return [nodes.raw('', parsed, format='html')]
|
|
||||||
register()
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Attrs(object):
|
|
||||||
ATTRS = {}
|
|
||||||
|
|
||||||
|
|
||||||
class CustomHTMLTranslator(HTMLTranslator):
|
|
||||||
"""
|
|
||||||
Base class for reST files translations.
|
|
||||||
There are couple of customizations for docinfo fields behaviour and
|
|
||||||
abbreviations and acronyms.
|
|
||||||
"""
|
|
||||||
def __init__(self, document):
|
|
||||||
"""
|
|
||||||
Set some nice defaults for articles translations
|
|
||||||
"""
|
|
||||||
HTMLTranslator.__init__(self, document)
|
|
||||||
self.initial_header_level = 4
|
|
||||||
|
|
||||||
def visit_section(self, node):
|
|
||||||
"""
|
|
||||||
Don't affect document, just keep track of the section levels
|
|
||||||
"""
|
|
||||||
self.section_level += 1
|
|
||||||
|
|
||||||
def depart_section(self, node):
|
|
||||||
self.section_level -= 1
|
|
||||||
|
|
||||||
def visit_meta(self, node):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def depart_meta(self, node):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def visit_document(self, node):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def depart_document(self, node):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def depart_docinfo(self, node):
|
|
||||||
"""
|
|
||||||
Reset body, remove unnecessary content.
|
|
||||||
"""
|
|
||||||
self.body = []
|
|
||||||
|
|
||||||
def visit_date(self, node):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def depart_date(self, node):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def visit_literal(self, node):
|
|
||||||
"""
|
|
||||||
This is almos the same as the original one from HTMLTranslator class.
|
|
||||||
The only difference is in used HTML tag: it uses 'code' instead of
|
|
||||||
'tt'
|
|
||||||
"""
|
|
||||||
self.body.append(self.starttag(node, 'code', ''))
|
|
||||||
text = node.astext()
|
|
||||||
for token in self.words_and_spaces.findall(text):
|
|
||||||
if token.strip():
|
|
||||||
# Protect text like "--an-option" and the regular expression
|
|
||||||
# ``[+]?(\d+(\.\d*)?|\.\d+)`` from bad line wrapping
|
|
||||||
if self.sollbruchstelle.search(token):
|
|
||||||
self.body.append('<span class="pre">%s</span>'
|
|
||||||
% self.encode(token))
|
|
||||||
else:
|
|
||||||
self.body.append(self.encode(token))
|
|
||||||
elif token in ('\n', ' '):
|
|
||||||
# Allow breaks at whitespace:
|
|
||||||
self.body.append(token)
|
|
||||||
else:
|
|
||||||
# Protect runs of multiple spaces; the last space can wrap:
|
|
||||||
self.body.append(' ' * (len(token) - 1) + ' ')
|
|
||||||
self.body.append('</code>')
|
|
||||||
# Content already processed:
|
|
||||||
raise nodes.SkipNode
|
|
||||||
|
|
||||||
def visit_acronym(self, node):
|
|
||||||
"""
|
|
||||||
Define missing acronym HTML tag
|
|
||||||
"""
|
|
||||||
node_text = node.children[0].astext()
|
|
||||||
node_text = node_text.replace('\n', ' ')
|
|
||||||
patt = re.compile(r'^(.+)\s<(.+)>')
|
|
||||||
|
|
||||||
if patt.match(node_text):
|
|
||||||
node.children[0] = nodes.Text(patt.match(node_text).groups()[0])
|
|
||||||
self.body.append(\
|
|
||||||
self.starttag(node, 'acronym',
|
|
||||||
'', title=patt.match(node_text).groups()[1]))
|
|
||||||
|
|
||||||
else:
|
|
||||||
self.body.append(self.starttag(node, 'acronym', ''))
|
|
||||||
|
|
||||||
def visit_abbreviation(self, node):
|
|
||||||
"""
|
|
||||||
Define missing abbr HTML tag
|
|
||||||
"""
|
|
||||||
node_text = node.children[0].astext()
|
|
||||||
node_text = node_text.replace('\n', ' ')
|
|
||||||
patt = re.compile(r'^(.+)\s<(.+)>')
|
|
||||||
|
|
||||||
if patt.match(node_text):
|
|
||||||
node.children[0] = nodes.Text(patt.match(node_text).groups()[0])
|
|
||||||
self.body.append(\
|
|
||||||
self.starttag(node, 'abbr',
|
|
||||||
'', title=patt.match(node_text).groups()[1]))
|
|
||||||
|
|
||||||
else:
|
|
||||||
self.body.append(self.starttag(node, 'abbr', ''))
|
|
||||||
|
|
||||||
|
|
||||||
class NoHeaderHTMLTranslator(CustomHTMLTranslator):
|
|
||||||
"""
|
|
||||||
Special subclass for generating only body of an article
|
|
||||||
"""
|
|
||||||
def __init__(self, document):
|
|
||||||
"""
|
|
||||||
Remove all needless parts of HTML document.
|
|
||||||
"""
|
|
||||||
CustomHTMLTranslator.__init__(self, document)
|
|
||||||
self.head = []
|
|
||||||
self.meta = []
|
|
||||||
self.head_prefix = ['', '', '', '', '']
|
|
||||||
self.body_prefix = []
|
|
||||||
self.body_suffix = []
|
|
||||||
self.stylesheet = []
|
|
||||||
self.generator = ('')
|
|
||||||
|
|
||||||
def visit_field(self, node):
|
|
||||||
"""
|
|
||||||
Harvest docinfo fields and store it in global dictionary.
|
|
||||||
"""
|
|
||||||
key, val = [n.astext() for n in node]
|
|
||||||
Attrs.ATTRS[key.lower()] = val.strip()
|
|
||||||
|
|
||||||
def visit_date(self, node):
|
|
||||||
"""
|
|
||||||
Store published date in global dictionary.
|
|
||||||
"""
|
|
||||||
Attrs.ATTRS['date'] = node.astext()
|
|
||||||
|
|
||||||
|
|
||||||
class PreviewHTMLTranslator(CustomHTMLTranslator):
|
|
||||||
"""
|
|
||||||
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
|
|
||||||
stylesheets. Note, that style_custom is present only locally to adjust
|
|
||||||
way of display the page
|
|
||||||
"""
|
|
||||||
CustomHTMLTranslator.__init__(self, document)
|
|
||||||
self.initial_header_level = 1
|
|
||||||
self.section_level = 1
|
|
||||||
# order of css files is important
|
|
||||||
self.default_stylesheets = PreviewHTMLTranslator.CSS
|
|
||||||
self.stylesheet = [self.stylesheet_link % self.encode(css) \
|
|
||||||
for css in self.default_stylesheets]
|
|
||||||
self.body_ = []
|
|
||||||
|
|
||||||
def depart_docinfo(self, node):
|
|
||||||
"""
|
|
||||||
Overwrite body with some custom one. body_ will hold the first heading
|
|
||||||
with title of the document.
|
|
||||||
"""
|
|
||||||
self.body = self.body_
|
|
||||||
|
|
||||||
def visit_field(self, node):
|
|
||||||
"""
|
|
||||||
Make title visible as a heading
|
|
||||||
"""
|
|
||||||
key, node_ = [n.astext() for n in node]
|
|
||||||
key = key.lower()
|
|
||||||
if key == 'title':
|
|
||||||
self.head.append('<title>%s</title>\n' % self.encode(node_))
|
|
||||||
self.body_.append('<h1 class="post-title entry-title">'
|
|
||||||
'<a href="#">%s</a></h1>\n' % self.encode(node_))
|
|
||||||
|
|
||||||
|
|
||||||
class BlogBodyWriter(Writer):
|
|
||||||
"""
|
|
||||||
Custom Writer class for generating HTML partial with the article
|
|
||||||
"""
|
|
||||||
def __init__(self):
|
|
||||||
Writer.__init__(self)
|
|
||||||
self.translator_class = NoHeaderHTMLTranslator
|
|
||||||
|
|
||||||
def translate(self):
|
|
||||||
self.document.settings.output_encoding = "utf-8"
|
|
||||||
Writer.translate(self)
|
|
||||||
|
|
||||||
|
|
||||||
class BlogPreviewWriter(Writer):
|
|
||||||
"""
|
|
||||||
Custom Writer class for generating full HTML of the article
|
|
||||||
"""
|
|
||||||
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, stylesheets=None):
|
|
||||||
"""
|
|
||||||
Returns full HTML of the article.
|
|
||||||
string argument is an article in reST
|
|
||||||
"""
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
def blogArticleString(string):
|
|
||||||
"""
|
|
||||||
Returns partial HTML of the article, and attribute dictionary
|
|
||||||
string argument is an article in reST
|
|
||||||
"""
|
|
||||||
# reset ATTRS
|
|
||||||
Attrs.ATTRS = {}
|
|
||||||
html_output = core.publish_string(string, writer=BlogBodyWriter())
|
|
||||||
html_output = html_output.strip()
|
|
||||||
html_output = html_output.replace("<!-- more -->", "\n<!-- more -->\n")
|
|
||||||
attrs = {}
|
|
||||||
for key in Attrs.ATTRS:
|
|
||||||
if Attrs.ATTRS[key]:
|
|
||||||
attrs[key] = Attrs.ATTRS[key]
|
|
||||||
|
|
||||||
return html_output, attrs
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
"""
|
|
||||||
Generate CSS stylesheet out of provided Style class module, which is an output
|
|
||||||
from the vim2pygments.py[1] script.
|
|
||||||
|
|
||||||
That stylesheet (with any necessary, additional modifications) can be used
|
|
||||||
with vimblogger_ft[2] VIm plugin, but also for general pygments usage.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
style2css <module>
|
|
||||||
|
|
||||||
[1] vim2pygments is part of the Pygments module, and can be found in scripts
|
|
||||||
directory of the Pygments <http://pygments.org> distribution.
|
|
||||||
[2] <http://www.vim.org/scripts/script.php?script_id=3367>
|
|
||||||
"""
|
|
||||||
|
|
||||||
import optparse
|
|
||||||
import os
|
|
||||||
|
|
||||||
from pygments.formatters import HtmlFormatter
|
|
||||||
|
|
||||||
|
|
||||||
class Pygments2CSS(object):
|
|
||||||
def __init__(self, modulefn, cssclass):
|
|
||||||
self.style_class = None
|
|
||||||
self.cssclass = cssclass
|
|
||||||
if not self.cssclass.startswith("."):
|
|
||||||
self.cssclass = "." + self.cssclass
|
|
||||||
|
|
||||||
mod = os.path.splitext(os.path.basename(modulefn))[0]
|
|
||||||
|
|
||||||
try:
|
|
||||||
module = __import__("%s" % mod)
|
|
||||||
except ImportError:
|
|
||||||
print('Error: %s should be in PYTHONPATH or current'
|
|
||||||
' directory, and should contain valid Style derived'
|
|
||||||
' class' % modulefn)
|
|
||||||
raise
|
|
||||||
|
|
||||||
for item in dir(module):
|
|
||||||
if item != 'Style' and item.endswith('Style'):
|
|
||||||
self.style_class = getattr(module, item)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
raise ValueError("Error: Wrong module?")
|
|
||||||
|
|
||||||
def out(self):
|
|
||||||
formatter = HtmlFormatter(style=self.style_class)
|
|
||||||
print "%s { background-color: %s }" % \
|
|
||||||
(self.cssclass, self.style_class.background_color)
|
|
||||||
for line in formatter.get_style_defs().split("\n"):
|
|
||||||
print "%s" % self.cssclass, line
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
parser = optparse.OptionParser("usage: %prog [options] stylefile.py\n"
|
|
||||||
"Where stylefile.py is a product of the"
|
|
||||||
" vim2pygments.py script Pygments "
|
|
||||||
"distribution.")
|
|
||||||
parser.add_option("-c", "--class",
|
|
||||||
dest="cssclass",
|
|
||||||
type="str",
|
|
||||||
help="Main CSS class name. Defaults to 'syntax'",
|
|
||||||
default="syntax")
|
|
||||||
(options, args) = parser.parse_args()
|
|
||||||
|
|
||||||
if len(args) != 1:
|
|
||||||
parser.error("stylefile.py is required")
|
|
||||||
|
|
||||||
if not (os.path.exists(args[0]) and os.path.isfile(args[0])):
|
|
||||||
parser.error("%s not found" % args[0])
|
|
||||||
|
|
||||||
p2css = Pygments2CSS(args[0], options.cssclass)
|
|
||||||
p2css.out()
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
# module rst2blogger.tests
|
|
||||||
@@ -1,267 +0,0 @@
|
|||||||
# vim: set fileencoding=utf-8
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
from datetime import datetime
|
|
||||||
from tempfile import mkstemp
|
|
||||||
|
|
||||||
|
|
||||||
LOGIN = "John"
|
|
||||||
PASS = "secret"
|
|
||||||
REST_ARTICLE = u""":Title: Title — This is a test
|
|
||||||
:Date: 2010-12-12T12:36:36+01:00
|
|
||||||
:Tags: this is a test, Blogger, rest
|
|
||||||
|
|
||||||
.. meta::
|
|
||||||
:description: meta are completely ignored in blogger parsers
|
|
||||||
|
|
||||||
`Amet`, convallis sollicitudin, commodo a, purus. Nulla vitae eros a diam
|
|
||||||
blandit **mollis**. Proin luctus ``ls --color ~/`` feugiat eros.
|
|
||||||
|
|
||||||
.. more
|
|
||||||
|
|
||||||
Pellentesque habitant morbi tristique senectus et *netus* et malesuada fames
|
|
||||||
ac turpis egestas. Duis ultricies urna: ``easy_install pygments``. Etiam enim
|
|
||||||
urna, pharetra suscipit, varius et, congue quis, odio. Donec `NES <Nintendo
|
|
||||||
Entertainment System>`:acronym: lobortis, elit bibendum euismod faucibus,
|
|
||||||
velit nibh egestas libero, vitae pellentesque elit augue ut massa.
|
|
||||||
|
|
||||||
test empty `acronym`:acronym: and `abbrev`:abbreviation:
|
|
||||||
|
|
||||||
Section 1
|
|
||||||
---------
|
|
||||||
|
|
||||||
Nulla consequat erat at massa. Vivamus id mi. Morbi purus enim, dapibus a,
|
|
||||||
facilisis non, tincidunt at, enim. Vestibulum ante ipsum primis in faucibus
|
|
||||||
orci luctus et ultrices posuere cubilia Curae; `WTF? <What the
|
|
||||||
fcuk?>`:abbreviation: Duis imperdiet eleifend arcu. Cras magna ligula,
|
|
||||||
consequat at, tempor non, posuere.
|
|
||||||
|
|
||||||
Subsection 1.1
|
|
||||||
..............
|
|
||||||
|
|
||||||
.. sourcecode:: python
|
|
||||||
|
|
||||||
import vim
|
|
||||||
print vim.current.buffer.name
|
|
||||||
|
|
||||||
.. sourcecode:: unknown_lexer
|
|
||||||
|
|
||||||
Cras dignissim vulputate metus.
|
|
||||||
Phasellus eu quam. Quisque interdum cursus purus. In.
|
|
||||||
|
|
||||||
End.
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class Eval(object):
|
|
||||||
"""
|
|
||||||
Communication class
|
|
||||||
"""
|
|
||||||
value = ""
|
|
||||||
blog = None
|
|
||||||
gdata_delete = 0
|
|
||||||
|
|
||||||
|
|
||||||
class Dummy(sys.__class__):
|
|
||||||
"""
|
|
||||||
Dummy class, for faking modules and other objects, not directly needed
|
|
||||||
"""
|
|
||||||
def __getattr__(self, attrname):
|
|
||||||
""" The dummy class should have no attribute """
|
|
||||||
if attrname == 'util':
|
|
||||||
return Dummy("util")
|
|
||||||
return None
|
|
||||||
|
|
||||||
# fake vim module.
|
|
||||||
sys.modules["vim"] = Dummy("vim")
|
|
||||||
|
|
||||||
|
|
||||||
class MockBuffer(list):
|
|
||||||
"""
|
|
||||||
Vim buffer-like class
|
|
||||||
"""
|
|
||||||
def append(self, val, line=None):
|
|
||||||
"""
|
|
||||||
Override append method to mimic vim.buffer append behaviour
|
|
||||||
"""
|
|
||||||
if line is None:
|
|
||||||
super(MockBuffer, self).append(val)
|
|
||||||
else:
|
|
||||||
super(MockBuffer, self).insert(line, val)
|
|
||||||
|
|
||||||
|
|
||||||
class Mock(object):
|
|
||||||
"""
|
|
||||||
Generic all-purpose mock class
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
import vim
|
|
||||||
vim.command = lambda x: None
|
|
||||||
vim.current = Mock()
|
|
||||||
vim.current.buffer = MockBuffer(REST_ARTICLE.split("\n"))
|
|
||||||
fdesc, vim.current.buffer.name = mkstemp()
|
|
||||||
vim.current.buffer.name += ".rst"
|
|
||||||
os.close(fdesc) # close descriptor, only filename is needed
|
|
||||||
|
|
||||||
|
|
||||||
def mock_vim_eval(string):
|
|
||||||
ints = ("g:blogger_draft", "g:blogger_maxarticles",
|
|
||||||
"g:blogger_confirm_del")
|
|
||||||
if string in ints:
|
|
||||||
return "0"
|
|
||||||
elif string == "g:blogger_stylesheets":
|
|
||||||
return []
|
|
||||||
else:
|
|
||||||
return Eval.value
|
|
||||||
vim.eval = mock_vim_eval
|
|
||||||
|
|
||||||
|
|
||||||
class MockBlog(object):
|
|
||||||
"""
|
|
||||||
Mock blog class
|
|
||||||
"""
|
|
||||||
def __init__(self, name, id):
|
|
||||||
self.name = name
|
|
||||||
self.id = id
|
|
||||||
|
|
||||||
def get_blog_name(self):
|
|
||||||
return self.name
|
|
||||||
|
|
||||||
def get_blog_id(self):
|
|
||||||
return self.id
|
|
||||||
|
|
||||||
def get_post_link(self):
|
|
||||||
link = Mock()
|
|
||||||
link.href = "http://www.mock.org"
|
|
||||||
return link
|
|
||||||
|
|
||||||
def get_post_id(self):
|
|
||||||
return self.id
|
|
||||||
|
|
||||||
|
|
||||||
class MockPost(object):
|
|
||||||
"""
|
|
||||||
Mock class imitating posts
|
|
||||||
"""
|
|
||||||
def __init__(self):
|
|
||||||
self.category = Mock()
|
|
||||||
self.category = []
|
|
||||||
self.id = None
|
|
||||||
self.title = Mock()
|
|
||||||
self.title.text = ""
|
|
||||||
self.published = Mock()
|
|
||||||
self.published.text = ""
|
|
||||||
|
|
||||||
def add_label(self, label):
|
|
||||||
item = Mock()
|
|
||||||
item.term = label
|
|
||||||
self.category.append(item)
|
|
||||||
|
|
||||||
def get_post_id(self):
|
|
||||||
return self.id
|
|
||||||
|
|
||||||
|
|
||||||
class MockBlogFeed(object):
|
|
||||||
"""
|
|
||||||
Mock class for feed objects
|
|
||||||
"""
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.entry = []
|
|
||||||
if Eval.blog:
|
|
||||||
for bid, bname in {1: 'one', 3: 'test', 7: 'blog_name'}.items():
|
|
||||||
blog = MockBlog(bname, bid)
|
|
||||||
self.entry.append(blog)
|
|
||||||
|
|
||||||
|
|
||||||
class MockPostFeed(object):
|
|
||||||
"""
|
|
||||||
Mock class for feed objects
|
|
||||||
"""
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
self.entry = []
|
|
||||||
|
|
||||||
|
|
||||||
from atom.data import Id, Updated
|
|
||||||
from gdata.blogger.client import BloggerClient
|
|
||||||
|
|
||||||
BloggerClient.get_blogs = lambda x: MockBlogFeed()
|
|
||||||
|
|
||||||
from gdata.client import BadAuthentication
|
|
||||||
|
|
||||||
|
|
||||||
def mock_client_login(self, login, password, source=None, service=None):
|
|
||||||
"""
|
|
||||||
Mock method for client login.
|
|
||||||
"""
|
|
||||||
if login != LOGIN or password != PASS:
|
|
||||||
raise BadAuthentication("Incorrect username or password")
|
|
||||||
BloggerClient.client_login = mock_client_login
|
|
||||||
|
|
||||||
|
|
||||||
def mock_client_post(self, post, url=None):
|
|
||||||
"""
|
|
||||||
Mimic post method
|
|
||||||
"""
|
|
||||||
if Eval.value == 10:
|
|
||||||
return None
|
|
||||||
new_id = Id(text='1234567890')
|
|
||||||
post.id = new_id
|
|
||||||
date = datetime.utcnow()
|
|
||||||
milli = str(date.microsecond)[:3]
|
|
||||||
date = date.strftime("%Y-%m-%dT%H:%M:%S")
|
|
||||||
date = date + ".%s+00:00" % milli
|
|
||||||
post.updated = Updated(text=date)
|
|
||||||
return post
|
|
||||||
BloggerClient.post = mock_client_post
|
|
||||||
BloggerClient.update = mock_client_post
|
|
||||||
|
|
||||||
|
|
||||||
def mock_client_delete(self, post):
|
|
||||||
"""
|
|
||||||
Mock delete method
|
|
||||||
"""
|
|
||||||
if not post:
|
|
||||||
raise AttributeError("%s object has no attribute 'etag'" % type(post))
|
|
||||||
if Eval.gdata_delete:
|
|
||||||
return "404 Mock"
|
|
||||||
return None
|
|
||||||
BloggerClient.delete = mock_client_delete
|
|
||||||
|
|
||||||
|
|
||||||
def mock_client_get_posts(self, blog_id):
|
|
||||||
"""
|
|
||||||
Mock get_posts method
|
|
||||||
"""
|
|
||||||
posts = (('title1', 1, "2000-01-01T00:04:00.001+01:00"),
|
|
||||||
('title2', 2, "2001-01-01T00:02:19.001+01:00"),
|
|
||||||
('title3', 3, "2002-01-01T00:01:00.001+01:00"),
|
|
||||||
('title4', 4, "2006-01-01T00:02:00.001+02:00"))
|
|
||||||
feed = MockPostFeed()
|
|
||||||
for p in posts:
|
|
||||||
a = MockPost()
|
|
||||||
a.id = p[1]
|
|
||||||
a.title.text = p[0]
|
|
||||||
a.published.text = p[2]
|
|
||||||
feed.entry.append(a)
|
|
||||||
return feed
|
|
||||||
BloggerClient.get_posts = mock_client_get_posts
|
|
||||||
|
|
||||||
|
|
||||||
def mock_client_get_feed(self, uri, desired_class=None):
|
|
||||||
"""
|
|
||||||
Mock get_feed method
|
|
||||||
"""
|
|
||||||
post = MockPost()
|
|
||||||
post.add_label('test1')
|
|
||||||
return post
|
|
||||||
BloggerClient.get_feed = mock_client_get_feed
|
|
||||||
|
|
||||||
|
|
||||||
from gdata.blogger.data import BlogPost
|
|
||||||
|
|
||||||
|
|
||||||
def mock_get_post_id(self):
|
|
||||||
return self.id.text
|
|
||||||
BlogPost.get_post_id = mock_get_post_id
|
|
||||||
@@ -1,514 +0,0 @@
|
|||||||
import os
|
|
||||||
import sys
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
this_dir = os.path.dirname(os.path.abspath(__file__))
|
|
||||||
this_dir = os.path.abspath(os.path.join(this_dir, "../.."))
|
|
||||||
sys.path.insert(0, this_dir)
|
|
||||||
|
|
||||||
from rst2blogger.tests import shared
|
|
||||||
from rst2blogger.blogger import VimBlogger
|
|
||||||
|
|
||||||
|
|
||||||
class TestCheckDates(unittest.TestCase):
|
|
||||||
"""
|
|
||||||
Tests for method VimBlogger._check_date
|
|
||||||
"""
|
|
||||||
def setUp(self):
|
|
||||||
"""
|
|
||||||
Create VimBlogger object
|
|
||||||
"""
|
|
||||||
self.vimb = VimBlogger(None, shared.LOGIN, shared.PASS)
|
|
||||||
|
|
||||||
def test_happy_case_CET(self):
|
|
||||||
"""
|
|
||||||
Test on good date string on Central and East Europe
|
|
||||||
"""
|
|
||||||
date = "2000-01-01T00:00:00.001+01:00"
|
|
||||||
self.assertTrue(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
def test_happy_case_HST(self):
|
|
||||||
"""
|
|
||||||
Test on good date string on Hawaii Time Zone
|
|
||||||
"""
|
|
||||||
date = "2000-01-01T00:00:00.001-10:00"
|
|
||||||
self.assertTrue(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
def test_happy_case_GMT(self):
|
|
||||||
"""
|
|
||||||
Test UTC date string
|
|
||||||
"""
|
|
||||||
date = "2000-01-01T00:00:00.001-00:00"
|
|
||||||
self.assertTrue(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
def test_without_milliseconds(self):
|
|
||||||
"""
|
|
||||||
Test on date string without milliseconds
|
|
||||||
"""
|
|
||||||
date = "2000-01-01T00:00:00+01:00"
|
|
||||||
self.assertTrue(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
def test_wrong_tz_format(self):
|
|
||||||
"""
|
|
||||||
Test date with wrong timezone format (hour have no leading 0)
|
|
||||||
"""
|
|
||||||
date = "2000-01-01T00:00:00.001+1:00"
|
|
||||||
self.assertFalse(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
# Test date with wrong timezone format (minute have only one digit)
|
|
||||||
date = "2000-01-01T00:00:00.001+01:0"
|
|
||||||
self.assertFalse(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
# Test date with wrong timezone format (hours and minutes hasn't been
|
|
||||||
# separated by colon)
|
|
||||||
date = "2000-01-01T00:00:00.001+0100"
|
|
||||||
self.assertFalse(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
def test_wrong_milliseconds(self):
|
|
||||||
"""
|
|
||||||
Test date with wrong format of milliseconds (.01 instead of .010)
|
|
||||||
"""
|
|
||||||
date = "2000-01-01T00:00:00.01+01:00"
|
|
||||||
self.assertFalse(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
# Test date with wrong format of milliseconds (.1 instead of .100)
|
|
||||||
date = "2000-01-01T00:00:00.1+01:00"
|
|
||||||
self.assertFalse(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
# Test date with spolied format (dot for milliseconds, but no digits)
|
|
||||||
date = "2000-01-01T00:00:00.+01:00"
|
|
||||||
self.assertFalse(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
def test_good_milliseconds(self):
|
|
||||||
"""
|
|
||||||
Test date with correct format of milliseconds
|
|
||||||
"""
|
|
||||||
date = "2000-01-01T00:00:00.000+01:00"
|
|
||||||
self.assertTrue(self.vimb._check_date(date), date + " is incorrect")
|
|
||||||
|
|
||||||
date = "2000-01-01T00:00:00.999+01:00"
|
|
||||||
self.assertTrue(self.vimb._check_date(date), date + " is incorrect")
|
|
||||||
|
|
||||||
def test_wrong_hours(self):
|
|
||||||
"""
|
|
||||||
Test date with wrong hours value
|
|
||||||
"""
|
|
||||||
date = "2000-01-01T24:00:00.001+01:00"
|
|
||||||
self.assertFalse(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
def test_good_hours(self):
|
|
||||||
"""
|
|
||||||
Test date with correct hours values
|
|
||||||
"""
|
|
||||||
date = "2000-01-01T00:00:00.001+01:00"
|
|
||||||
self.assertTrue(self.vimb._check_date(date), date + " is incorrect")
|
|
||||||
date = "2000-01-01T23:00:00.001+01:00"
|
|
||||||
self.assertTrue(self.vimb._check_date(date), date + " is incorrect")
|
|
||||||
|
|
||||||
def test_wrong_minutes(self):
|
|
||||||
"""
|
|
||||||
Test date with wrong minutes value
|
|
||||||
"""
|
|
||||||
date = "2000-01-01T00:60:00.001+01:00"
|
|
||||||
self.assertFalse(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
date = "2000-01-01T00:000:00.001+01:00"
|
|
||||||
self.assertFalse(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
date = "2000-01-01T00:1:00.001+01:00"
|
|
||||||
self.assertFalse(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
def test_good_minutes(self):
|
|
||||||
"""
|
|
||||||
Test date with correct minutes values
|
|
||||||
"""
|
|
||||||
date = "2000-01-01T00:01:00.001+01:00"
|
|
||||||
self.assertTrue(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
date = "2000-01-01T00:59:00.001+01:00"
|
|
||||||
self.assertTrue(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
def test_wrong_seconds(self):
|
|
||||||
"""
|
|
||||||
Test date with wrong seconds value
|
|
||||||
"""
|
|
||||||
date = "2000-01-01T00:00:60.001+01:00"
|
|
||||||
self.assertFalse(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
def test_good_seconds(self):
|
|
||||||
"""
|
|
||||||
Test date with good seconds values
|
|
||||||
"""
|
|
||||||
for second in range(60):
|
|
||||||
date = "2000-01-01T00:00:%0.2d.001+01:00" % second
|
|
||||||
self.assertTrue(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
def test_wrong_days(self):
|
|
||||||
"""
|
|
||||||
Test date with incorrect days (january has always 31 days, no month
|
|
||||||
has lower number than 1)
|
|
||||||
"""
|
|
||||||
date = "2000-01-32T00:00:00.001+01:00"
|
|
||||||
self.assertFalse(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
date = "2000-01-00T00:00:00.001+01:00"
|
|
||||||
self.assertFalse(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
def test_good_days(self):
|
|
||||||
"""
|
|
||||||
Test date with correct days (january has always 31 days)
|
|
||||||
"""
|
|
||||||
date = "2000-01-01T00:00:00.001+01:00"
|
|
||||||
self.assertTrue(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
date = "2000-01-31T00:00:00.001+01:00"
|
|
||||||
self.assertTrue(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
def test_wrong_month(self):
|
|
||||||
"""
|
|
||||||
Test date with wrong month
|
|
||||||
"""
|
|
||||||
date = "2000-00-01T00:00:00.001+01:00"
|
|
||||||
self.assertFalse(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
date = "2000-13-01T00:00:00.001+01:00"
|
|
||||||
self.assertFalse(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
date = "2000-1-01T00:00:00.001+01:00"
|
|
||||||
self.assertFalse(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
date = "2000-001-01T00:00:00.001+01:00"
|
|
||||||
self.assertFalse(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
def test_good_month(self):
|
|
||||||
"""
|
|
||||||
Test date with correct months
|
|
||||||
"""
|
|
||||||
date = "2000-01-01T00:00:00.001+01:00"
|
|
||||||
self.assertTrue(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
date = "2000-12-01T00:00:00.001+01:00"
|
|
||||||
self.assertTrue(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
def test_wrong_year(self):
|
|
||||||
"""
|
|
||||||
Test date with wrong year
|
|
||||||
"""
|
|
||||||
date = "0000-01-01T00:00:00.001+01:00"
|
|
||||||
self.assertFalse(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
date = "10000-01-01T00:00:00.001+01:00"
|
|
||||||
self.assertFalse(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
date = "900-01-01T00:00:00.001+01:00"
|
|
||||||
self.assertFalse(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
def test_good_year(self):
|
|
||||||
"""
|
|
||||||
Test date with correct years
|
|
||||||
"""
|
|
||||||
date = "0001-01-01T00:00:00.001+01:00"
|
|
||||||
self.assertTrue(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
date = "9999-01-01T00:00:00.001+01:00"
|
|
||||||
self.assertTrue(self.vimb._check_date(date))
|
|
||||||
|
|
||||||
|
|
||||||
class TestAuthorize(unittest.TestCase):
|
|
||||||
"""
|
|
||||||
Test method VimBlogger._authorize
|
|
||||||
"""
|
|
||||||
def setUp(self):
|
|
||||||
"""
|
|
||||||
Create VimBlogger object (with good credentials, yes :>)
|
|
||||||
"""
|
|
||||||
self.vimob = VimBlogger(None, shared.LOGIN, shared.PASS)
|
|
||||||
|
|
||||||
def test_happy_case(self):
|
|
||||||
"""
|
|
||||||
Try to login with good credentials
|
|
||||||
"""
|
|
||||||
self.assertTrue(self.vimob._authorize(shared.LOGIN,
|
|
||||||
shared.PASS) is None)
|
|
||||||
|
|
||||||
def test_wrong_login(self):
|
|
||||||
"""
|
|
||||||
Try to login with wrong login
|
|
||||||
"""
|
|
||||||
self.assertRaises(shared.BadAuthentication, self.vimob._authorize,
|
|
||||||
'joe', shared.PASS)
|
|
||||||
|
|
||||||
def test_wrong_pass(self):
|
|
||||||
"""
|
|
||||||
Try to login with wrong password
|
|
||||||
"""
|
|
||||||
self.assertRaises(shared.BadAuthentication, self.vimob._authorize,
|
|
||||||
'joe', shared.PASS)
|
|
||||||
|
|
||||||
|
|
||||||
class TestAddTag(unittest.TestCase):
|
|
||||||
"""
|
|
||||||
Test method VimBlogger._add_tag
|
|
||||||
"""
|
|
||||||
def setUp(self):
|
|
||||||
"""
|
|
||||||
Create VimBlogger object
|
|
||||||
"""
|
|
||||||
self.vimob = VimBlogger(None, shared.LOGIN, shared.PASS)
|
|
||||||
self.post = shared.MockPost()
|
|
||||||
|
|
||||||
def test_add_tag(self):
|
|
||||||
"""
|
|
||||||
Add items to existing categories. List should be uniq.
|
|
||||||
"""
|
|
||||||
self.vimob._add_tag(self.post, 'item')
|
|
||||||
self.assertTrue(len(self.post.category) == 1)
|
|
||||||
|
|
||||||
# Item number should not change on the same label
|
|
||||||
self.vimob._add_tag(self.post, 'item')
|
|
||||||
self.assertTrue(len(self.post.category) == 1)
|
|
||||||
|
|
||||||
self.vimob._add_tag(self.post, 'item2')
|
|
||||||
self.assertTrue(len(self.post.category) == 2)
|
|
||||||
|
|
||||||
|
|
||||||
class TestExtractDate(unittest.TestCase):
|
|
||||||
"""
|
|
||||||
Test method VimBlogger._extract_date
|
|
||||||
"""
|
|
||||||
def setUp(self):
|
|
||||||
"""
|
|
||||||
Create VimBlogger object
|
|
||||||
"""
|
|
||||||
self.vimob = VimBlogger(None, shared.LOGIN, shared.PASS)
|
|
||||||
|
|
||||||
def test_extract_date(self):
|
|
||||||
"""
|
|
||||||
Date should be already verified by _check_date method, so only
|
|
||||||
extraction is tested
|
|
||||||
"""
|
|
||||||
date = "2000-01-01T00:00:00.001-10:00"
|
|
||||||
|
|
||||||
# wrong scenario
|
|
||||||
self.assertFalse(self.vimob._extract_date('wrong_date_string'))
|
|
||||||
|
|
||||||
# only date should be returned
|
|
||||||
self.assertEqual(self.vimob._extract_date(date), "2000-01-01")
|
|
||||||
|
|
||||||
# date and time should be returned
|
|
||||||
self.assertEqual(self.vimob._extract_date(date, True),
|
|
||||||
"2000-01-01 00:00:00")
|
|
||||||
|
|
||||||
|
|
||||||
class TestGetPost(unittest.TestCase):
|
|
||||||
"""
|
|
||||||
Test method VimBlogger._get_post
|
|
||||||
"""
|
|
||||||
def setUp(self):
|
|
||||||
"""
|
|
||||||
Create VimBlogger object
|
|
||||||
"""
|
|
||||||
self.vimob = VimBlogger(None, shared.LOGIN, shared.PASS)
|
|
||||||
self.vimob.blog = shared.Mock()
|
|
||||||
|
|
||||||
link = shared.Mock()
|
|
||||||
link.href = "mock.com"
|
|
||||||
link.feed = shared.Mock()
|
|
||||||
|
|
||||||
self.vimob.blog.get_post_link = lambda: link
|
|
||||||
|
|
||||||
def test_get_post(self):
|
|
||||||
"""
|
|
||||||
Nothing really to test here. Maybe in the future :)
|
|
||||||
"""
|
|
||||||
result = self.vimob._get_post('1234')
|
|
||||||
self.assertEqual(type(result), shared.MockPost)
|
|
||||||
|
|
||||||
|
|
||||||
class TestSetBlog(unittest.TestCase):
|
|
||||||
"""
|
|
||||||
Test method VimBlogger._set_blog
|
|
||||||
"""
|
|
||||||
def setUp(self):
|
|
||||||
"""
|
|
||||||
Create VimBlogger object
|
|
||||||
"""
|
|
||||||
self.vimob = VimBlogger(None, shared.LOGIN, shared.PASS)
|
|
||||||
for bid, bname in {1: 'one', 3: 'test', 7: 'blog_name'}.items():
|
|
||||||
blog = shared.MockBlog(bname, bid)
|
|
||||||
self.vimob.feed.entry.append(blog)
|
|
||||||
|
|
||||||
def test_set_blog(self):
|
|
||||||
"""
|
|
||||||
Test setting a blog
|
|
||||||
"""
|
|
||||||
self.vimob._set_blog("no_valid_blog_name")
|
|
||||||
self.assertEqual(self.vimob.blog_id, None)
|
|
||||||
self.assertEqual(self.vimob.blog, None)
|
|
||||||
|
|
||||||
self.vimob._set_blog("blog_name")
|
|
||||||
self.assertEqual(self.vimob.blog_id, 7)
|
|
||||||
self.assertEqual(self.vimob.blog.get_blog_name(), 'blog_name')
|
|
||||||
|
|
||||||
self.vimob._set_blog("test")
|
|
||||||
self.assertEqual(self.vimob.blog_id, 3)
|
|
||||||
self.assertEqual(self.vimob.blog.get_blog_name(), 'test')
|
|
||||||
|
|
||||||
self.vimob._set_blog("one")
|
|
||||||
self.assertEqual(self.vimob.blog_id, 1)
|
|
||||||
self.assertEqual(self.vimob.blog.get_blog_name(), 'one')
|
|
||||||
|
|
||||||
|
|
||||||
class TestCreateArticle(unittest.TestCase):
|
|
||||||
"""
|
|
||||||
Test method VimBlogger.create_article
|
|
||||||
"""
|
|
||||||
def setUp(self):
|
|
||||||
"""
|
|
||||||
Create VimBlogger object
|
|
||||||
"""
|
|
||||||
self.vimob = VimBlogger(None, shared.LOGIN, shared.PASS)
|
|
||||||
|
|
||||||
def test_create_simple_article(self):
|
|
||||||
"""
|
|
||||||
Test creation of article with minimum requirements
|
|
||||||
"""
|
|
||||||
html = "<p>article</p>"
|
|
||||||
post = self.vimob.create_article(html)
|
|
||||||
self.vimob.draft = True
|
|
||||||
|
|
||||||
self.assertEqual(post.id.text, '1234567890')
|
|
||||||
self.assertEqual(post.content.text, html)
|
|
||||||
self.assertEqual(post.published, None)
|
|
||||||
self.assertTrue(post.updated is not None)
|
|
||||||
self.assertEqual(post.title.text, "")
|
|
||||||
self.assertEqual(post.category, [])
|
|
||||||
self.assertEqual(post.control.draft.text, "yes")
|
|
||||||
|
|
||||||
def test_create_article(self):
|
|
||||||
"""
|
|
||||||
Test creation of article with full attrs
|
|
||||||
"""
|
|
||||||
html = u"<p>article \xe2\x80\x94 article</p>"
|
|
||||||
labels = "tag with spaces|vim|python|blogger".split("|")
|
|
||||||
attrs = {"title": u'Title \xe2\x80\x94 title',
|
|
||||||
"tags": ", ".join(labels),
|
|
||||||
"date": "2010-12-10T14:18:32+00:00"}
|
|
||||||
self.vimob.draft = False
|
|
||||||
|
|
||||||
post = self.vimob.create_article(html, attrs)
|
|
||||||
self.assertEqual(post.id.text, '1234567890')
|
|
||||||
self.assertEqual(post.content.text, html)
|
|
||||||
self.assertEqual(post.published.text, attrs['date'])
|
|
||||||
self.assertTrue(post.updated is not None)
|
|
||||||
self.assertEqual(post.title.text, attrs['title'])
|
|
||||||
self.assertEqual(len(post.category), 4)
|
|
||||||
|
|
||||||
for label in post.category:
|
|
||||||
self.assertTrue(label.term in labels)
|
|
||||||
del(labels[labels.index(label.term)])
|
|
||||||
|
|
||||||
self.assertEqual(post.control, None)
|
|
||||||
|
|
||||||
|
|
||||||
class TestDeleteArticle(unittest.TestCase):
|
|
||||||
"""
|
|
||||||
Test method VimBlogger.create_article
|
|
||||||
"""
|
|
||||||
def setUp(self):
|
|
||||||
"""
|
|
||||||
Create VimBlogger object
|
|
||||||
"""
|
|
||||||
self.vimob = VimBlogger(None, shared.LOGIN, shared.PASS)
|
|
||||||
for bid, bname in {1: 'one', 3: 'test', 7: 'blog_name'}.items():
|
|
||||||
blog = shared.MockBlog(bname, bid)
|
|
||||||
self.vimob.feed.entry.append(blog)
|
|
||||||
self.vimob._set_blog('test')
|
|
||||||
|
|
||||||
def test_delete_non_existing_article(self):
|
|
||||||
"""
|
|
||||||
Test removing article without id
|
|
||||||
"""
|
|
||||||
self.assertEqual(self.vimob.delete_article(None),
|
|
||||||
"No article id provided")
|
|
||||||
|
|
||||||
def test_delete_article(self):
|
|
||||||
"""
|
|
||||||
Test removing article
|
|
||||||
"""
|
|
||||||
html = u"<p>article \xe2\x80\x94 article</p>"
|
|
||||||
labels = "tag with spaces|vim|python|blogger".split("|")
|
|
||||||
attrs = {"title": u'Title \xe2\x80\x94 title',
|
|
||||||
"tags": ", ".join(labels),
|
|
||||||
"date": "2010-12-10T14:18:32+00:00"}
|
|
||||||
self.vimob.draft = False
|
|
||||||
|
|
||||||
post = self.vimob.create_article(html, attrs)
|
|
||||||
self.assertEqual(self.vimob.delete_article(post.id.text), None)
|
|
||||||
|
|
||||||
|
|
||||||
class TestGetArticles(unittest.TestCase):
|
|
||||||
"""
|
|
||||||
Test method VimBlogger.get_articles
|
|
||||||
"""
|
|
||||||
def setUp(self):
|
|
||||||
"""
|
|
||||||
Create VimBlogger object
|
|
||||||
"""
|
|
||||||
self.vimob = VimBlogger(None, shared.LOGIN, shared.PASS)
|
|
||||||
|
|
||||||
def test_get_articles(self):
|
|
||||||
"""
|
|
||||||
Test removing article without id
|
|
||||||
"""
|
|
||||||
articles = self.vimob.get_articles()
|
|
||||||
self.assertEqual(len(articles), 4)
|
|
||||||
|
|
||||||
articles = self.vimob.get_articles(maxarticles=2)
|
|
||||||
self.assertEqual(len(articles), 2)
|
|
||||||
|
|
||||||
|
|
||||||
class TestUpdateArticle(unittest.TestCase):
|
|
||||||
"""
|
|
||||||
Test method VimBlogger.update_article
|
|
||||||
"""
|
|
||||||
def setUp(self):
|
|
||||||
"""
|
|
||||||
Create VimBlogger object
|
|
||||||
"""
|
|
||||||
self.vimob = VimBlogger(None, shared.LOGIN, shared.PASS)
|
|
||||||
for bid, bname in {1: 'one', 3: 'test', 7: 'blog_name'}.items():
|
|
||||||
blog = shared.MockBlog(bname, bid)
|
|
||||||
self.vimob.feed.entry.append(blog)
|
|
||||||
self.vimob._set_blog('test')
|
|
||||||
|
|
||||||
def test_wrong_argument_types(self):
|
|
||||||
"""
|
|
||||||
Test update_article method with wrong argument types
|
|
||||||
"""
|
|
||||||
self.assertRaises(TypeError, self.vimob.update_article, None, None)
|
|
||||||
|
|
||||||
def test_no_id_in_attrs(self):
|
|
||||||
"""
|
|
||||||
Test update_article method with no id in attrs
|
|
||||||
"""
|
|
||||||
self.assertRaises(Exception, self.vimob.update_article,
|
|
||||||
'<p>update</p>', [])
|
|
||||||
|
|
||||||
def test_update(self):
|
|
||||||
"""
|
|
||||||
Test update_article method with no id in attrs
|
|
||||||
"""
|
|
||||||
attrs = {'id': 1234567890, 'title': 'update',
|
|
||||||
'date': '2001-01-01T00:02:19.001+01:00',
|
|
||||||
'tags': "tag1, tag2, tag3"}
|
|
||||||
post = self.vimob.update_article('<p>update</p>', attrs)
|
|
||||||
|
|
||||||
self.assertEqual(post.title.text, 'update')
|
|
||||||
self.assertEqual(post.id.text, '1234567890')
|
|
||||||
self.assertEqual(post.content.text, '<p>update</p>')
|
|
||||||
self.assertTrue(post.updated.text is not None)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
unittest.main()
|
|
||||||
@@ -1,295 +0,0 @@
|
|||||||
# vim: set fileencoding=utf-8
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import unittest
|
|
||||||
import webbrowser
|
|
||||||
|
|
||||||
webbrowser.open = lambda x: None
|
|
||||||
|
|
||||||
this_dir = os.path.dirname(os.path.abspath(__file__))
|
|
||||||
this_dir = os.path.abspath(os.path.join(this_dir, "../.."))
|
|
||||||
sys.path.insert(0, this_dir)
|
|
||||||
|
|
||||||
from rst2blogger.tests.shared import LOGIN, PASS, Eval, MockBuffer
|
|
||||||
from rst2blogger.main import Rst2Blogger
|
|
||||||
from gdata.client import BadAuthentication
|
|
||||||
|
|
||||||
|
|
||||||
class TestRst2Blogger(unittest.TestCase):
|
|
||||||
"""
|
|
||||||
Tests for vim - rest - blogger interface
|
|
||||||
"""
|
|
||||||
def setUp(self):
|
|
||||||
"""
|
|
||||||
Create Rst2Blogger object
|
|
||||||
"""
|
|
||||||
self.obj = Rst2Blogger()
|
|
||||||
|
|
||||||
def test_object_creation(self):
|
|
||||||
"""
|
|
||||||
Create Rst2Blogger object and test it.
|
|
||||||
"""
|
|
||||||
self.assertTrue(self.obj is not None)
|
|
||||||
self.assertEqual(self.obj.docinfo_len, 3)
|
|
||||||
self.assertEqual(self.obj.login, "")
|
|
||||||
self.assertEqual(self.obj.password, "")
|
|
||||||
self.assertEqual(self.obj.blogname, "")
|
|
||||||
self.assertEqual(self.obj.buffer_encoding, "")
|
|
||||||
self.assertEqual(self.obj.vim_encoding, "")
|
|
||||||
self.assertEqual(self.obj.maxarticles, 0)
|
|
||||||
self.assertEqual(self.obj.draft, 0)
|
|
||||||
self.assertEqual(self.obj.confirm_del, 0)
|
|
||||||
self.assertEqual(self.obj.stylesheets, [])
|
|
||||||
|
|
||||||
|
|
||||||
class TestRst2BloggerSetDocinfoLen(unittest.TestCase):
|
|
||||||
"""
|
|
||||||
Test _set_docinfo_len method on different docinfo configurations
|
|
||||||
"""
|
|
||||||
def setUp(self):
|
|
||||||
"""
|
|
||||||
Create Rst2Blogger object
|
|
||||||
"""
|
|
||||||
self.obj = Rst2Blogger()
|
|
||||||
|
|
||||||
def test_set_docinfo_len(self):
|
|
||||||
"""
|
|
||||||
Test with no defined docinfo
|
|
||||||
"""
|
|
||||||
self.obj.buff = self.obj.buff[4:]
|
|
||||||
self.obj._set_docinfo_len()
|
|
||||||
self.assertEqual(self.obj.docinfo_len, 0)
|
|
||||||
|
|
||||||
def test_set_docinfo_len2(self):
|
|
||||||
"""
|
|
||||||
Test with one docinfo entry
|
|
||||||
"""
|
|
||||||
self.obj.buff = self.obj.buff[:1] + [''] + self.obj.buff[4:]
|
|
||||||
self.obj._set_docinfo_len()
|
|
||||||
self.assertEqual(self.obj.docinfo_len, 1)
|
|
||||||
|
|
||||||
def test_set_docinfo_len3(self):
|
|
||||||
"""
|
|
||||||
Test with wrong docinfo definition
|
|
||||||
"""
|
|
||||||
self.obj.buff = self.obj.buff[:1] + self.obj.buff[4:]
|
|
||||||
self.obj._set_docinfo_len()
|
|
||||||
self.assertEqual(self.obj.docinfo_len, 0)
|
|
||||||
|
|
||||||
|
|
||||||
class TestCheckHtml(unittest.TestCase):
|
|
||||||
"""
|
|
||||||
Check HTML parser
|
|
||||||
"""
|
|
||||||
def setUp(self):
|
|
||||||
"""
|
|
||||||
Create Rst2Blogger object
|
|
||||||
"""
|
|
||||||
self.obj = Rst2Blogger()
|
|
||||||
|
|
||||||
def test_check_html1(self):
|
|
||||||
"""
|
|
||||||
Parse (generated) html string, should return empty string
|
|
||||||
"""
|
|
||||||
html = "<html><head><title>test</title></head><body></body></html>"
|
|
||||||
self.assertEqual(self.obj._check_html(html), "")
|
|
||||||
self.assertEqual(self.obj._check_html(html, True), "")
|
|
||||||
|
|
||||||
def test_check_html2(self):
|
|
||||||
"""
|
|
||||||
Parse html fragment string
|
|
||||||
"""
|
|
||||||
html = "<p>first paragraph</p><p>another paragraph</p>"
|
|
||||||
self.assertEqual(self.obj._check_html(html),
|
|
||||||
"junk after document element: line 1, column 22")
|
|
||||||
self.assertEqual(self.obj._check_html(html, True), "")
|
|
||||||
|
|
||||||
def test_check_html3(self):
|
|
||||||
"""
|
|
||||||
Parse wrong html string (crossed tags)
|
|
||||||
"""
|
|
||||||
html = "<p>first paragraph<b></p>another paragraph</b>"
|
|
||||||
self.assertEqual(self.obj._check_html(html),
|
|
||||||
"mismatched tag: line 1, column 23")
|
|
||||||
self.assertEqual(self.obj._check_html(html, True),
|
|
||||||
"mismatched tag: line 1, column 28")
|
|
||||||
|
|
||||||
|
|
||||||
class TestRst2BloggerDelete(unittest.TestCase):
|
|
||||||
"""
|
|
||||||
Test delete method
|
|
||||||
"""
|
|
||||||
def setUp(self):
|
|
||||||
"""
|
|
||||||
Create Rst2Blogger object
|
|
||||||
"""
|
|
||||||
self.obj = Rst2Blogger()
|
|
||||||
self.obj.login = LOGIN
|
|
||||||
self.obj.password = PASS
|
|
||||||
self.obj.blogname = "test"
|
|
||||||
self.obj.vim_encoding = "utf-8"
|
|
||||||
|
|
||||||
def test_delete_without_password(self):
|
|
||||||
"""
|
|
||||||
Delete article, while password is incorrect/nonexistend
|
|
||||||
"""
|
|
||||||
self.obj.password = ""
|
|
||||||
self.assertRaises(BadAuthentication, self.obj.delete)
|
|
||||||
|
|
||||||
def test_delete(self):
|
|
||||||
"""
|
|
||||||
Delete article. Set confirmation attribute.
|
|
||||||
"""
|
|
||||||
self.obj.confirm_del = 1
|
|
||||||
Eval.value = 2 # set choice to answer "Y" for confirmation
|
|
||||||
Eval.blog = "test"
|
|
||||||
self.assertEqual(self.obj.delete(), "Article deleted")
|
|
||||||
|
|
||||||
def test_delete2(self):
|
|
||||||
"""
|
|
||||||
Delete article. Set confirmation attribute. Refuse to delete.
|
|
||||||
"""
|
|
||||||
self.obj.confirm_del = 1
|
|
||||||
Eval.value = 1 # set choice to answer "N" for confirmation
|
|
||||||
Eval.blog = "test"
|
|
||||||
self.assertEqual(self.obj.delete(), "No articles deleted")
|
|
||||||
|
|
||||||
def test_delete3(self):
|
|
||||||
"""
|
|
||||||
Delete article. Unset confirmation attribute. Delete returns something
|
|
||||||
else then None.
|
|
||||||
"""
|
|
||||||
Eval.value = 2
|
|
||||||
Eval.blog = "test"
|
|
||||||
Eval.gdata_delete = 1
|
|
||||||
self.assertEqual(self.obj.delete(), "Article deleted")
|
|
||||||
|
|
||||||
|
|
||||||
class TestRst2BloggerPost(unittest.TestCase):
|
|
||||||
"""
|
|
||||||
Test post method
|
|
||||||
"""
|
|
||||||
def setUp(self):
|
|
||||||
"""
|
|
||||||
Create Rst2Blogger object
|
|
||||||
"""
|
|
||||||
self.obj = Rst2Blogger()
|
|
||||||
self.obj.login = LOGIN
|
|
||||||
self.obj.password = PASS
|
|
||||||
self.obj.blogname = "test"
|
|
||||||
self.obj.vim_encoding = "utf-8"
|
|
||||||
self.obj.buffer_encoding = "utf-8"
|
|
||||||
# create copy of the buffer list and assign copy to the buff attribute
|
|
||||||
self._rest = MockBuffer(self.obj.buff[:])
|
|
||||||
self.obj.buff = self._rest
|
|
||||||
|
|
||||||
def test_without_password(self):
|
|
||||||
"""
|
|
||||||
Post article, while password is incorrect/nonexistend
|
|
||||||
"""
|
|
||||||
self.obj.password = ""
|
|
||||||
self.assertRaises(BadAuthentication, self.obj.post)
|
|
||||||
|
|
||||||
def test_with_wrong_data(self):
|
|
||||||
"""
|
|
||||||
Try to post not well formed html
|
|
||||||
"""
|
|
||||||
self.obj.buff.append('')
|
|
||||||
self.obj.buff.append('.. raw:: html')
|
|
||||||
self.obj.buff.append('')
|
|
||||||
self.obj.buff.append(' <p>foo<b>bar</p>baz</b>')
|
|
||||||
self.obj.buff.append('')
|
|
||||||
self.obj.post()
|
|
||||||
self.assertEqual(self.obj.post(),
|
|
||||||
'There are errors in generated document')
|
|
||||||
|
|
||||||
def test_post_create(self):
|
|
||||||
"""
|
|
||||||
Try to post well formed html, as a new article
|
|
||||||
"""
|
|
||||||
self.assertEqual(self.obj.post(),
|
|
||||||
'New article with id 1234567890 has been created')
|
|
||||||
|
|
||||||
def test_post_update(self):
|
|
||||||
"""
|
|
||||||
Try to post well formed html, as a new article
|
|
||||||
"""
|
|
||||||
self.obj.buff.append(':Id: 1234567890', 0)
|
|
||||||
self.assertEqual(self.obj.post(),
|
|
||||||
"Article 'Title \xe2\x80\x94 This is a test' "
|
|
||||||
"has been updated")
|
|
||||||
|
|
||||||
|
|
||||||
class TestRst2BloggerUpdateDocinfo(unittest.TestCase):
|
|
||||||
"""
|
|
||||||
Test _update_docinfo
|
|
||||||
"""
|
|
||||||
def setUp(self):
|
|
||||||
"""
|
|
||||||
Create Rst2Blogger object
|
|
||||||
"""
|
|
||||||
self.obj = Rst2Blogger()
|
|
||||||
self.obj.login = LOGIN
|
|
||||||
self.obj.password = PASS
|
|
||||||
self.obj.blogname = "test"
|
|
||||||
self.obj.vim_encoding = "utf-8"
|
|
||||||
self.obj.buffer_encoding = "utf-8"
|
|
||||||
# create copy of the buffer list and assign copy to the buff attribute
|
|
||||||
self._rest = MockBuffer(self.obj.buff[:])
|
|
||||||
self.obj.buff = self._rest
|
|
||||||
|
|
||||||
def test_with_empty_docinfo(self):
|
|
||||||
"""
|
|
||||||
Try to post not well formed html
|
|
||||||
"""
|
|
||||||
self.obj.buff = MockBuffer(self.obj.buff[4:])
|
|
||||||
self.obj.docinfo_len = 0
|
|
||||||
self.obj._update_docinfo('title', 'title2')
|
|
||||||
|
|
||||||
|
|
||||||
class TestRst2BloggerPreview(unittest.TestCase):
|
|
||||||
"""
|
|
||||||
Test preview
|
|
||||||
"""
|
|
||||||
def setUp(self):
|
|
||||||
"""
|
|
||||||
Create Rst2Blogger object
|
|
||||||
"""
|
|
||||||
self.obj = Rst2Blogger()
|
|
||||||
self.obj.login = LOGIN
|
|
||||||
self.obj.password = PASS
|
|
||||||
self.obj.blogname = "test"
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
"""
|
|
||||||
Remove leftovers in fs
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
os.unlink(self.obj.buff.name[:-4])
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
os.unlink(self.obj.buff.name[:-4] + ".html")
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_preview_open_in_browser(self):
|
|
||||||
"""
|
|
||||||
Try to post not well formed html
|
|
||||||
"""
|
|
||||||
Eval.value = 1
|
|
||||||
self.assertEqual(self.obj.preview(),
|
|
||||||
'Generated HTML has been opened in browser')
|
|
||||||
|
|
||||||
def test_preview_save_to_file(self):
|
|
||||||
"""
|
|
||||||
Try to post not well formed html
|
|
||||||
"""
|
|
||||||
Eval.value = 0
|
|
||||||
name = self.obj.buff.name[:-4] + ".html"
|
|
||||||
self.assertEqual(self.obj.preview(),
|
|
||||||
"Generated HTML has been written to %s" % name)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
unittest.main()
|
|
||||||
@@ -1,201 +0,0 @@
|
|||||||
# vim: set fileencoding=utf-8
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import unittest
|
|
||||||
import re
|
|
||||||
|
|
||||||
this_dir = os.path.dirname(os.path.abspath(__file__))
|
|
||||||
this_dir = os.path.abspath(os.path.join(this_dir, "../.."))
|
|
||||||
sys.path.insert(0, this_dir)
|
|
||||||
|
|
||||||
from rst2blogger.rest import blogArticleString, blogPreview
|
|
||||||
from rst2blogger.tests.shared import REST_ARTICLE
|
|
||||||
|
|
||||||
LINENOS1 = """
|
|
||||||
.. sourcecode:: python
|
|
||||||
:linenos:
|
|
||||||
|
|
||||||
import vim
|
|
||||||
print vim.current.buffer.name
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
LINENOS2 = """
|
|
||||||
.. sourcecode:: python
|
|
||||||
:linenos: -1
|
|
||||||
|
|
||||||
import vim
|
|
||||||
print vim.current.buffer.name
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
LINENOS3 = """
|
|
||||||
.. sourcecode:: python
|
|
||||||
:linenos: 0
|
|
||||||
|
|
||||||
import vim
|
|
||||||
print vim.current.buffer.name
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
LINENOS4 = """
|
|
||||||
.. sourcecode:: python
|
|
||||||
:linenos: 12
|
|
||||||
|
|
||||||
import vim
|
|
||||||
print vim.current.buffer.name
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
LINENOS5 = """
|
|
||||||
.. sourcecode:: python
|
|
||||||
:linenos: this is wrong
|
|
||||||
|
|
||||||
import vim
|
|
||||||
print vim.current.buffer.name
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
CSSCLASS1 = """
|
|
||||||
.. sourcecode:: python
|
|
||||||
:cssclass:
|
|
||||||
|
|
||||||
import vim
|
|
||||||
print vim.current.buffer.name
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
CSSCLASS2 = """
|
|
||||||
.. sourcecode:: python
|
|
||||||
:cssclass: Dessert256
|
|
||||||
|
|
||||||
import vim
|
|
||||||
print vim.current.buffer.name
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class TestBlogPreview(unittest.TestCase):
|
|
||||||
"""
|
|
||||||
Test generating HTML out of prepared reST text. It tests only for some
|
|
||||||
aspects of the entire thing, because it is not intendend to test all of
|
|
||||||
reST directives.
|
|
||||||
"""
|
|
||||||
def test_content(self):
|
|
||||||
"""
|
|
||||||
Simple case, check output
|
|
||||||
"""
|
|
||||||
html_out = blogPreview(REST_ARTICLE)
|
|
||||||
self.assertTrue(len(html_out) > 0)
|
|
||||||
self.assertTrue("<html" in html_out)
|
|
||||||
self.assertTrue("</html>" in html_out)
|
|
||||||
self.assertTrue("<?xml version=\"1.0\" encoding=\"utf-8\"" in
|
|
||||||
html_out)
|
|
||||||
self.assertTrue("\n\n<!-- more -->\n\n" in html_out)
|
|
||||||
self.assertTrue("<title>Title — This is a test</title>" in html_out)
|
|
||||||
self.assertTrue('type="text/css"' not in html_out)
|
|
||||||
self.assertTrue(re.search(r"<h1.*><a href=\"#\">Title — This is a"
|
|
||||||
" test</a></h1>", html_out))
|
|
||||||
self.assertTrue(re.search(r"<h2>Section 1</h2>", html_out))
|
|
||||||
self.assertTrue(re.search(r"<h3>Subsection 1.1</h3>", html_out))
|
|
||||||
self.assertTrue("description" not in html_out)
|
|
||||||
|
|
||||||
def test_stylesheets(self):
|
|
||||||
"""
|
|
||||||
Test output for stylesheets
|
|
||||||
"""
|
|
||||||
html_out = blogPreview(REST_ARTICLE, ["css/style1.css",
|
|
||||||
"css/blogger1.css"])
|
|
||||||
self.assertTrue('type="text/css"' in html_out)
|
|
||||||
match = re.search(r'<link rel="stylesheet" '
|
|
||||||
'href=".*" type="text/css" />', html_out)
|
|
||||||
self.assertTrue(match is not None)
|
|
||||||
self.assertEqual(len(match.span()), 2)
|
|
||||||
|
|
||||||
|
|
||||||
class TestBlogArticleString(unittest.TestCase):
|
|
||||||
"""
|
|
||||||
Test blogArticleString function, wich should return part of html and
|
|
||||||
dictionary with attributes.
|
|
||||||
"""
|
|
||||||
def test_blogArticleString(self):
|
|
||||||
html_out, attrs = blogArticleString(REST_ARTICLE)
|
|
||||||
self.assertEqual(len(attrs), 3)
|
|
||||||
self.assertTrue(len(html_out) > 0)
|
|
||||||
self.assertTrue("<html" not in html_out)
|
|
||||||
self.assertTrue("</html>" not in html_out)
|
|
||||||
self.assertTrue("<?xml version=\"1.0\" encoding=\"utf-8\"" not in
|
|
||||||
html_out)
|
|
||||||
self.assertTrue("\n\n<!-- more -->\n\n" in html_out)
|
|
||||||
self.assertTrue("<title>Title — This is a test</title>" not in
|
|
||||||
html_out)
|
|
||||||
self.assertTrue('type="text/css"' not in html_out)
|
|
||||||
self.assertTrue(re.search(r"<h4>Section 1</h4>", html_out))
|
|
||||||
self.assertTrue(re.search(r"<h5>Subsection 1.1</h5>", html_out))
|
|
||||||
self.assertTrue("description" not in html_out)
|
|
||||||
|
|
||||||
self.assertEqual(attrs['title'], u"Title — This is a test")
|
|
||||||
self.assertEqual(attrs['date'], "2010-12-12T12:36:36+01:00")
|
|
||||||
self.assertEqual(attrs['tags'], "this is a test, Blogger, rest")
|
|
||||||
|
|
||||||
|
|
||||||
class TestBlogArticlePytgments(unittest.TestCase):
|
|
||||||
"""
|
|
||||||
Test cases for sourcecode directive
|
|
||||||
"""
|
|
||||||
def test_linenos_no_args(self):
|
|
||||||
"""
|
|
||||||
Test linenos option with no additional arguments
|
|
||||||
"""
|
|
||||||
html_out, _ = blogArticleString(LINENOS1)
|
|
||||||
self.assertTrue('<pre><span class="lineno">1</span>' in html_out)
|
|
||||||
|
|
||||||
def test_linenos_with_arg1(self):
|
|
||||||
"""
|
|
||||||
Test linenos option with correct argument type but wrong value.
|
|
||||||
Should count from 1 in this case.
|
|
||||||
"""
|
|
||||||
html_out, _ = blogArticleString(LINENOS2)
|
|
||||||
self.assertTrue('<pre><span class="lineno">1</span>' in html_out)
|
|
||||||
|
|
||||||
def test_linenos_with_arg2(self):
|
|
||||||
"""
|
|
||||||
Test linenos option with correct argument type but wrong value.
|
|
||||||
Should count from 1 in this case.
|
|
||||||
"""
|
|
||||||
html_out, _ = blogArticleString(LINENOS3)
|
|
||||||
self.assertTrue('<pre><span class="lineno">1</span>' in html_out)
|
|
||||||
|
|
||||||
def test_linenos_with_arg3(self):
|
|
||||||
"""
|
|
||||||
Test linenos option with correct argument type and correct value.
|
|
||||||
Should count from 1 in this case.
|
|
||||||
"""
|
|
||||||
html_out, _ = blogArticleString(LINENOS4)
|
|
||||||
self.assertTrue('<pre><span class="lineno">12</span>' in html_out)
|
|
||||||
|
|
||||||
def test_linenos_with_wrong_arg(self):
|
|
||||||
"""
|
|
||||||
Test linenos option with wrong argument type. Should count from 1.
|
|
||||||
"""
|
|
||||||
html_out, _ = blogArticleString(LINENOS5)
|
|
||||||
self.assertTrue('<pre><span class="lineno">1</span>' in html_out)
|
|
||||||
|
|
||||||
def test_cssclass_failure(self):
|
|
||||||
"""
|
|
||||||
Test cssclass option with no arguments. Should complain with system
|
|
||||||
message.
|
|
||||||
"""
|
|
||||||
html_out, _ = blogArticleString(CSSCLASS1)
|
|
||||||
self.assertTrue('System Message: ERROR/3' in html_out)
|
|
||||||
|
|
||||||
def test_cssclass_correct(self):
|
|
||||||
"""
|
|
||||||
Test cssclass option with Dessert256 as an argument. Should be used as
|
|
||||||
a main div CSS class.
|
|
||||||
"""
|
|
||||||
html_out, _ = blogArticleString(CSSCLASS2)
|
|
||||||
self.assertTrue('<div class="Dessert256">' in html_out)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
unittest.main()
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
" reST to blogger vim interface.
|
|
||||||
" Provide some convinient commands for creating preview from the reST file
|
|
||||||
" and to send articles to blog.
|
|
||||||
" VERSION: 0.2
|
|
||||||
|
|
||||||
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 = ""
|
|
||||||
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
|
|
||||||
|
|
||||||
if !exists("g:blogger_pygments_class")
|
|
||||||
let g:blogger_pygments_class = ""
|
|
||||||
endif
|
|
||||||
|
|
||||||
python << EOF
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import vim
|
|
||||||
|
|
||||||
scriptdir = os.path.dirname(vim.eval('expand("<sfile>")'))
|
|
||||||
sys.path.insert(0, scriptdir)
|
|
||||||
|
|
||||||
# Will raise exception, if one of required moudles is missing
|
|
||||||
from rst2blogger.main import Rst2Blogger
|
|
||||||
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
|
|
||||||
Reference in New Issue
Block a user