Monday, October 30, 2006

Retrieving Your BloggerBeta Blog ID

Some people are wondering how to programatically get the blogID that the Google Data API page keeps referring to. I'm not sure if this is the best way or not, but I've had success using the following method in Python:

def getBlogID(uri):
    import httplib2, re
    con = httplib2.Http()
    response, content = con.request(uri, 'GET')
    match = re.search('blogID=(\d*)', content)
    if match:
        return match.group(1)
    else:
        print "BlogID retrieval failed."

It's quite simple actually, it takes your blog's URL as a parameter (as in http://whatever.blogspot.com), and sends an empty GET request to it. The response returned contains a string that matches the regular expression blogID=(\d*). That is, the string "blogID=" followed by a bunch of numbers. That bunch of numbers is the blogID. The function shown above extracts that number with a regular expression, and returns it.

Friday, October 27, 2006

GNOME-Terminal Vim with 256 Colors

All this time I've been using Vim in 8 color mode assuming that my terminal (gnome-terminal) didn't support anything better. Well when using libvte version 0.13.0 or newer, it can support up to 256 colors! To pull this off in Vim is actually quite simple. You just need to tell Vim that your terminal can handle it, then use an appropriate colorscheme that takes advantage of the extra terminal colors. Telling Vim is as easy as set t_Co=256. After that download a colorscheme like desert256 to make it look all pretty-like.

Modified desert256

There's a screenshot of my Vim, running in the terminal, using a modified version of desert256. The only modifications I made were to tone down the comments a bit (from cyan), and to alter the omni-completion menu from face-melting hot pink. If I didn't know better, I'd think that was GVim :)

Tuesday, October 24, 2006

Mastering the Vim Help System

From hanging out in #vim on the Freenode IRC channel one thing I've noticed is that although the majority of people asking for support are more than willing to read the appropriate help topic, they are not familiar enough with the available tools to do so effectively. The Vim help system is surprisingly powerful, very well written, and very complete. This post is written to expose the Vim user to some of the less known tips about finding help within Vim.

The Basics

Given a command, or option, or any given topic that you'd like to find more information on, the best place to start looking for help is with the :help command. For example if you want to look up details on the expandtab option you would type :help expandtab to get to the appropriate section as shown below.

{@align=center}Expandtab Help Screenshot

Navigation With Bookmarks

Throughout the help documentation, you will find many terms surrounded by | symbols like |these-ones|. These terms are references to bookmarks. If you put the cursor on one of these references and press <C-]>, you will jump to that reference's corresponding bookmark. Bookmarks in the documentation are surrounded by * characters like *these-ones*. You can jump from topic to topic as much as you like, and pressing <C-T> at any time will take you backwards one step.

In fact, you can try the <C-]> trick on any word in the help system to attempt a jump to a help section on that word. The explicit references are there to take you to a specific destination though.

Help Completion

Ensure that that wildmenu option is turned on (typing :set wildmenu will enable it) and you can have some nice help topic <Tab> completion. By typing :h (Note: :h is short for :help) followed by a partial word and a <Tab> you will be shown a list of help topics that begin with the given pattern. Pressing <Tab> repeatedly here will cycle through the option. Press <Enter> to select the topic you want. The following screenshot is the result of typing :h expa<Tab>.

{@align=center}Tab-Completed Help

Another useful tip when you only know a partial name is <C-D> (Ctrl-D) completion. By typing a partial name and pressing <C-D>, you will get a list of available topics containing that partial word. For example, the following picture is a result of typing :h cursor<C-D>.

{@align=center}Ctl-D Help Completion

Search Help Using Regular Expressions

For more complex searches, we can use the :helpgrep command. This command takes a regular expression as an argument and will return a list of all occurrences of a match found in the help documentation. For example, if you wanted to find all occurrences of the term double-click you would type :helpgrep double-click. The result of this command is shown below.

{@align=center}HelpGrep Help Result

As you can see in the bottom of that screenshot, there were a total of 14 occurrences of this search term in the documentation. Each of these occurrences are displayed to you one at a time. To cycle to the next occurrence, use the :cn command. To cycle backwards, use the :cp command.

Keep Your Own Tips Section

This section doesn't cover more tools for searching the help documentation, but instead it suggests a way to take advantage of the help system itself to keep track of your own tips. This is a method I've been using for about a month now, and I've found it extremely helpful.

Start by creating a new text file in your ~/.vim/doc/ directory. Let's call it mytips.txt. Put the following line as the very first line of the new file.

*mytips.txt*  My own set of tips...

This is really the only line that matters as far as format goes. The rest of the file can be pretty much free form, but you will benefit by keeping it relatively organized. I keep mine divided up in sections, each section beginning with a bookmark like *mytips-movement*. With these bookmarks, I can jump to these sections immediately using the :h mytips-movement command just as I can jump anywhere else in the help system. In order to make your new file work as a help document, you need to run a command to generate a tags file for it like this: :helptags ~/.vim/d/oc

I have several autocommands in my ~/.vimrc to make this process a little more automated. If you'd like to use them, stick the following lines in your ~/.vimrc.

autocmd BufWrite mytips.txt             :helptags ~/.vim/doc/
autocmd BufRead  mytips.txt             set filetype=help
autocmd BufRead  mytips.txt             set noreadonly
autocmd BufRead  mytips.txt             set modifiable

nmap <leader>h :tabnew ~/.vim/doc/mytips.txt<CR>

The first one generates a new helptags file each time you write your tips file to disk. This keeps you from having to do it manually, and ensures your tips will always be current. The second sets the filetype to 'help' so that you get the appropriate syntax highlighting when you edit your file, and the following two undo a couple of unwanted options that setting the filetype enables automatically. Finally, <Leader>h is mapped to opening the new tips file in a new tab for editing (tabs are only in Vim7 or newer).

Now, when you discover a new tip, type <Leader>h (\h by default, see :he leader), add it to the file, and save it. Done!

Sunday, October 22, 2006

Vim-Blogger Featuring Markdown

Yes I'm still playing with this vim-blogger plugin. Now it has the ability to allow you to compose your post using John Gruber's Markdown syntax instead of plain ol' ugly html :). To accomplish this task, I am using a Python module for the conversion to HTML when posted.

Also cool is the fact that when a post is retrieved from your blog using this plugin, it is converted back to Markdown syntax using html2text. I think this is pretty cool. Here is a screenshot of me composing this post:

Vim-BloggerBeta with Markdown

As you can see, I have a Vim syntax file for Markdown files too. I've modified it a bit to include vim-blogger specific things like the @@LABELS@@ line at the bottom etc. I do plan on making a release of this plugin at some point soon. It's too buggy just yet though. When I do, it will be composed of multiple files including the markdown and html2text Python modules, the syntax files, an ftplugin directory, and a regular (small) blogger.vim plugin file.

Using Markdown syntax has both advantages and disadvantages. The biggest advantage is the fact that it is easier to read and write when compared to HTML code. Also, it is rapidly becoming a popular method of writing structured text. A disadvantage is that the syntax still has some weaknesses. For example as of now there is no way to specify the size of an embedded image and there's no way to make it float right or left in your text. This can be done with embedded HTML, but then it would get messed up during the conversion back to Markdown in the context of this Vim plugin. For now though, it's fun to play with :).

Saturday, October 21, 2006

Faster Buffer Switches in Vim

I've tried several times in the past to use various buffer management plugins like the very popular minibufexplorer and bufexplorer only to find myself not using them or forgetting to use them after a few days. It seems that the normal "switch by name or number" method is faster and more convenient for me for some unknown reason. I can see how these plugins could be useful if your current number of open buffers gets huge, but until then I think they just take up valuable screen real estate (I'm pretty stingy about my screen space).

Buffers inherit their names from their file names. You can switch buffers by entering :b {name} or by :b {N} where {name} is either the full or partial buffer name that you desire (filename), and {N} is the buffer number. This by default has a number of issues.

  • Buffer numbers are difficult to remember unless you are a robot. Especially if the number of buffers is large.
  • If you have two buffers named file1 and file2 and you attempt to switch buffers with a command like :b file you will get an error message stating that there is more than one buffer matching that pattern.
  • If you have a buffer named Foo and you attempt to switch to it with a command like :b foo it will not work. Switching buffers by name is case sensitive.
In order to overcome these problems, I decided to modify this behaviour a bit to make my preferred method of buffer switching a little more user friendly. Of course before I began writing a function for it, I did a quick search through the tips at the Vim website and found this tip by Cory T. Echols. As suggested in a comment in this tip, this function prompts the user for a choice even if there is only one match to the given pattern. Also, it doesn't solve the case sensitivity problem mentioned above. I decided to work from this function and modify it to better suit my own tastes. First, instead of having a boolean switch for whether a pattern match was found or not, I used a List (Cory's function was written well before Lists were available in Vim. They are a Vim 7 feature.) and appended the number of matching buffers to this list. Once I've cycled through all of the listed buffers, if the length of this list is 1, I immediately make that buffer the active one. Only if there are more than one matching buffers do I prompt the user for a choice. I also chose to exclude unlisted buffers from my search. Unlisted buffers are ones that do not show up in the :ls list because they have been closed with the :bdelete command. I figure if I delete the buffer then I don't want to use it anymore. I only want to select a buffer from the listed ones.

Finally, I added a global variable called g:BufSel_Case_Sensitive that controls whether the match is case sensitive or not. It defaults to false, but can be changed anywhere (like your ~/.vimrc for example). This makes it much easier to swap if your edited file names contain a mix of upper and lower case characters.

The final product looks like this:

function! BufSel(pattern)
    let buflist = []
    let bufcount = bufnr("$")
    let currbufnr = 1

    while currbufnr <= bufcount
        if(buflisted(currbufnr))
            let currbufname = bufname(currbufnr)
            if (exists("g:BufSel_Case_Sensitive") == 0 || g:BufSel_Case_Sensitive == 0)
                let curmatch = tolower(currbufname)
                let patmatch = tolower(a:pattern)
            else
                let curmatch = currbufname
                let patmatch = a:pattern
            endif
            if(match(curmatch, patmatch) > -1)
                call add(buflist, currbufnr)
            endif
        endif
        let currbufnr = currbufnr + 1
    endwhile
    if(len(buflist) > 1)
        for bufnum in buflist
            echo bufnum . ":      ". bufname(bufnum)
        endfor
        let desiredbufnr = input("Enter buffer number: ")
        if(strlen(desiredbufnr) != 0)
            exe ":bu ". desiredbufnr
        endif
    elseif (len(buflist) == 1)
        exe ":bu " . get(buflist,0)
    else
        echo "No matching buffers"
    endif
endfunction
command! -nargs=1 -complete=buffer Bs :call BufSel("<args>")
It's not a real complicated function, but it does add a lot of flexibility when switching buffers via the command line. I added the following line in my ~/.vimrc to replace the already existing command when typed:
    cabbr b Bs
So now when I type :b (notice the space) it is replaced by :Bs automatically and the function is run instead of the built-in buffer command. This suggestion was also taken from the comments in the tip page above, and it works quite well.

Friday, October 20, 2006

Making ZZ Behave in Vim

Update Apr 4, 2008: I was recently reminded of this post and thought I should mention that this particular solutions sucks. Although it won't break anything, it doesn't work well. Don't waste your time :) Cheers.

I've gotten quite used to the idea of hitting ZZ in Vim to kill the window I'm currently working in. The problem is that I'm so used to the idea, that I hit it almost automatically when I'm done with a particular buffer and there is only one window left. The result is that the Vim session is closed completely whether there is only one listed buffer or not.

My solution to this was not to find the probably existing command or setting built into Vim that corrects this problem, but rather to write a function that customizes the behavior of the ZZ command. This would be the first time I'd ever written a non-trivial function in pure Vim script, so I thought I'd give it a shot. The following function is the result. I've put lots of comments in there so that you can follow if you are new to Vim scripting like I am.

function! BehaveZZ()
    " Get the number of *listed* buffers.
    let highbuf = bufnr("$")
    let buflist = []
    let i = 1
    while (i <= highbuf)
        "Skip unlisted buffers.
        if (bufexists(i) != 0 && buflisted(i))
            call add(buflist, i)
        endif
        let i = i + 1
    endwhile
    let bufcount = len(buflist)
    if (bufcount == 1)
        if (bufname("%") == "")
            " This buffer is unnamed (has no associated file).
            if (&modified)
                " Give option to save modifications.
                let choice = input("Lose modifications? [Enter=yes]: ")
                if (choice == "")
                    set nomodified
                else
                    echo "ZZ action aborted..."
                    return
                endif
            else
                " The buffer has no modifications. Just do default ZZ.
                execute "x"
            endif
        else
            " There is only one listed buffer and it is named. In this case, 
            " the standard ZZ works just fine, so do that.
            execute "x"
        endif
    elseif (getbufvar(bufnr("%"), "&buftype") != "")
        " This buffer is a "special" buffer.
        execute "bdelete"
    elseif (bufname("%") == "")
        " This buffer is unnamed (has no associated file).
        if (&modified)
            " Give option to save modifications.
            let choice = input("Lose modifications? [Enter=yes]: ")
            if (choice == "")
                set nomodified
            else
                echo "ZZ action aborted..."
                return
            endif
        endif
        if (winnr("$") > 1)
            " There are multiple windows open. Just do a normal ZZ.
            execute "x"
        else
            execute "buffer! " . bufnr("#")
        endif
    else
        " This is a named buffer. 
        if (&modified)
            execute "write"
        endif
        if (winnr("$") > 1)
            " There are multiple windows. Just do normal ZZ.
            execute "x"
        else
            " There is only one window, but multiple listed buffers.
            let curbuf = bufnr("%")
            " If we have a 'last visited' buffer, go there. Else bnext.
            if (bufnr("#") != -1)
                execute "buffer! " . bufnr("#")
            else
                execute "bnext"
            endif
            execute "bdelete" . curbuf
        endif
    endif
endfunction
command! ZZ call BehaveZZ()

Now at the time of this writing, the above function has had very limited testing, so if you want to use it, beware. This is what the function does.

  • If there are multiple windows open, just close the current one. (Same as default ZZ)
  • If there is only one window open, but multiple buffers, delete the current buffer and switch to another one.
  • If there is only one window, and only one listed buffer, exit the session. (Same sd default ZZ)
  • If the current buffer is unnamed (ie. has no filename associated with it), confirm loss of modifications. If user presses just Enter, then discard modifications and delete the buffer. Else abort.

One behaviour of the default Vim that I found odd is a result of opening multiple files from the command line at once like vim file1 file2. When you do this, and try to close one you get a E173: 1 more file to edit message, and nothing happens at all. It however gives no such warnings if you open only one file, then open a second with the :e file2 command from inside the editor. This time it just closes the whole session if there is only one window open. In both of these cases, my function will close the currently active buffer and switch to one of the other buffers in the buffer list.

I think I've handled most special cases where a buffer is special like a help buffer, or a scratch buffer too. Initially I made it so that if the current buffer was unnamed and modified then the modifications would just be discarded, but that made me nervous so I added a one key confirmation just to be safe. The whole function turned out to be a lot more complex than I thought it would (not to mention longer). Things like special buffers gave me a lot of grief. Being new to this, the whole snippet probably ended up being more complicated than it needed to be. If you see any bugs or optimizations, I'd be interested in hearing about them. Leave a comment after this post.

To use this function, drop it into your ~/.vimrc and either :call it manually or map a key to it like

:map ZZ :call BehaveZZ()<CR>
. That's the mapping I use and it simply replaces ZZ's normal behavior with the new one.

Saturday, October 14, 2006

See the Vim-BloggerBeta Plugin in Action

I made a funky flash screencast of my vim-blogger plugin in action for all to see :). The screencast demonstrates saving a draft post, retrieving a list of all posts on the blog, editing a saved post, publishing a post, adding labels to a post, and finally deleting a post. The swf file ended up being quite large, but hopefully managable, and since Blogger doesn't allow hosting such files (yet?), I have to host it on my slow home server for now. Let me know if it is unbearably slow.

I do plan on releasing the plugin some day soon so all Vim/Blogger-beta users can make use of it, but first it needs some serious testing. The last thing I want to happen is for me to make it available too early, then have somebody pooch their data because of it. That and the thing is a bloody mess. I wrote it while discovering how to use the Blogger GData API, and the code shows it. Once it's in a safe state that I don't mind putting my name on, I'll make it public ;p.

Until then, enjoy the screencast, and leave comments or suggestions if you have any!

Friday, October 13, 2006

Success! Posting to Blogger-beta Using Vim

If you see this, then I've finally (at least partially) figured out how to use the Blogger-beta GData API from Python. And to make posting a little bit quicker/easier for me, I've stuck that Python code into a Vim plugin.

To be honest, I'm not entirely sure what I was doing wrong in my previous attempts. I've changed several things, then changed some back. The resulting code really doesn't look much different to me than it did before, but there are a few very subtle changes. I suspect my ignorance when it comes to HTTP and XML protocols played a large part in my frustrations.

Anyways, here is a working version of the Python code that works for posting a blog entry. You will obviously need to fill in your BLOGID, GMAIL_ADDRESS, and GMAIL_PASSWORD appropriately for it to work for you:

#!/usr/bin/env python

import httplib2, re

account = "GMAIL_ADDRESS"
password = "GMAIL_PASSWORD"
blogid = "BLOGID"

def authenticate(h):
    auth_uri = 'https://www.google.com/accounts/ClientLogin'
    headers = {'Content-Type': 'application/x-www-form-urlencoded'}
    myrequest = "Email=%s&Passwd=%s&service=blogger&service=TestCompany-TestApp-0.0" % (account, password)
    response, content = h.request(auth_uri, 'POST', body=myrequest, headers=headers)
    if response['status'] == '200':
        return re.search('Auth=(\S*)', content).group(1)
    else:
        return None

entry = """<?xml version="1.0" ?>
    <entry xmlns='http://www.w3.org/2005/Atom'>
      <title type='text'>Test Post</title>
      <content type='xhtml'>
        <div xmlns="http://www.w3.org/1999/xhtml">
        If you are reading this, then it worked!
        </div>
      </content>
      <author>
        <name>TestUser</name>
      </author>
    </entry>
    """

h = httplib2.Http()
uri = 'http://www.blogger.com/feeds/%s/posts/full' % blogid

# Get the Auth token from ClientLogin
auth = authenticate(h)
if auth:
    headers = {'Content-Type': 'application/atom+xml', 'Authorization': 'GoogleLogin auth=%s' % auth.strip()}
    response, content = h.request(uri, 'POST', body=entry, headers=headers)

    # blindly follow redirects
    while response['status'] == '302':
        response, content = h.request(response['location'], 'POST', body=entry, headers=headers)

    if response['status'] == '201':
        print "Entry successfully posted."
    else:
        print "Post failed: %s" % response['status']
else:
    print "Authorization failed."

It's pretty rough, I realize, but I think it's good enough to get anyone started if you are interested. I hope someone will find this useful.

Now to see what other functionality I can perform :).

Oh, I almost forgot. If you want the Vim plugin I'm using you can get it here. To install it, drop it in ~/.vimrc/plugins/. To use it, open vim, type your post's subject on the first line, the post body below that, and type :BlogPost when you are done.

Wednesday, October 11, 2006

Blogger Template Customization

Today I decided to customize my Blogger template a little bit to make it differ from the typical cookie cutter blogs on here. I'm not an expert on CSS and HTML by any means, but I think I'm pleased with the results so far. Aside from the shot of my ugly mug I stuck up there at the top of the page, I think it's fairly easy on the eyes. Of course, I've only viewed it with Firefox, so if you see any errors or problems with the page layout at all, please let me know.

By the looks of it, one pretty much has complete control over the look and feel of their Blogger-beta website. I could touch and edit pretty much every aspect of the page from what I could see. Like I said, however, I'm no expert so I didn't want to delve too deeply into things for fear of messing them up beyond repair.

A Step Closer to Google Authentication?

Thanks to a comment left by Frank Mantek regarding the issue I was having with my Blogger-beta authentication, I've been able to get a step closer (I think?) to success. Frank was correct in his assumption that I was getting a 302 redirect response from the ClientLogin URL, and that my custom headers weren't being resent to the new URL. The response I got was a dictionary which contained of course the status code of 302, and also a location key with a value of http://beta.blogger.com/feeds/BLOGID/posts/full. Being quite new to all of this HTTP stuff, I can only assume that location is where the redirect was pointing to. I then implemented a test on the return status code, if it is 302, I manually resent the post and custom headers to the new location, and got a new error!

The new error is a 400 Bad Request error, and the content says GoogleLogin auth token is malformed. A search online for that error string revealed a guy with the same problem, but no responses. Since that thread was too old to reply to and revive, I had to start a new and similar thread. Hopefully it'll get some action.

I won't post all of the new code as most of it remains unchanged. I simply modified the getPost() function to take the URL to make the request to. The new stuff looks like this, with the test for redirect included:

h = httplib2.Http()
uri = 'http://www.blogger.com/feeds/BLOGID/posts/full'

cert = authenticate()
if cert:
    response, content = postEntry(cert, uri)
    while response['status'] == '302':
        cert = authenticate()
        response, content = postEntry(cert, response['location'])
    print response, content

Hopefully this is in fact a step closer. I'd love to get this working. Thanks for the comment and the help, Frank! :)

Tuesday, October 10, 2006

Send a Gmail Message from Vim

Using Gmail on my laptop through the web interface can be quite time consuming. I use the Linux version of Firefox, and it's not exactly known around the world for its great speed. Anyways, sometimes I just want to shoot off a quick email, and it would take just as long to load the Gmail compose form as it would to type the email content. For these times I wrote a little vim-python script as follows:

" Make sure the Vim was compiled with +python before loading the script...
if !has("python")
        finish
endif

:command! -nargs=? GMSend :call GMailSend("<args>")

function! GMailSend(args)
python << EOF
import vim
to = vim.eval('a:args')
GSend(to)
EOF
endfunction

python << EOF
########### BEGIN USER CONFIG ##########
account = 'MY_GMAIL_ACCOUNT'
password = 'MY_GMAIL_PASSWORD'
########### END USER CONFIG ###########


def GSend(to):
    """
    Send the current buffer as a Gmail message to a given user.
    """
    import libgmail

    subject = vim.current.buffer[0]
    body = '\n'.join(vim.current.buffer[2:])

    ga = libgmail.GmailAccount(account, password)
    try:
        ga.login()
    except libgmail.GmailLoginFailure:
        print "Login failed. (Wrong username/password?)"

    gmsg = libgmail.GmailComposedMessage(to, subject, body)

    if ga.sendMessage(gmsg):
        print "Message sent `%s` successfully." % subject
    else:
        print "Could not send message."

EOF

It's a really simple script, and it uses Python so it will only work if your Vim was compiled with the +python option. Of course, you'll also need to edit the script to put in your own Gmail user name and password (lines 18 and 19).

Installation

Drop the above script (or download it from here) into your ~/.vim/plugin/ directory. Reload Vim or source the file with :so ~/.vim/gmail.vim.

Usage

It's even easier to use. The first line in the buffer is the email's subject, and the rest is the body. Once the email is composed in this fashion, type :GMSend <dest> where <dest> is the email address that you want to send this message to. If all is well, you'll see a message at the bottom of your Vim window indicating that all is well, and that the message was sent successfully.

Hopefully somebody out there will find this useful. The version I am actually using is a little more complex. I've extended the script to download messages by folder or label and stick the id, subject, and author in an active buffer. From there I can select one of the shown messages and the script will display the body in the same buffer. I've kept the rest to myself as I'm not real thrilled with the UI part, and I'm not sure it will work as expected for everyone. If you are interested, leave a comment. I'm pretty confident that the script posted above (the one that just sends mail) will work as expected. Enjoy!

Sunday, October 08, 2006

Using the Google GData API with Python

I did a very quick search online for examples that I could use to see how to interact with Google's GData API for creating and posting Blogger-beta blog entries. The best I could find was this post to Jon Udell's Infoworld weblog which illustrates making an entry to Google Calendar. Now even though it is a different web application, most of this example code is still relevent as apparently they use the same API.

From this, I tried the following code:

#!/usr/bin/env python

import httplib2

h = httplib2.Http()
h.add_credentials('MY_GMAIL_ACCOUNT', 'MY_GMAIL_PASSWORD')
h.follow_all_redirects = True
uri = 'http://www.blogger.com/feeds/BLOGID/posts/full'

post_xml = """
<?xml version="1.0" ?>
<entry xmlns='http://www.w3.org/2005/Atom'>
  <title type='text'>Test Post from Python</title>
  <content type='xhtml'>
    <div xmlns="http://www.w3.org/1999/xhtml">
      <p>If you can see this, then it worked!</p>
    </div>
  </content>
  <author>
    <name>Dennis</name>
  </author>
</entry>
"""

headers = {'Content-Type': 'application/atom+xml'}
response, content = h.request(uri, 'POST', body=post_xml, headers=headers)
print response, content

With this code, the last print line revealed a Error 401 of Missing auth parameter in GoogleLogin. I assumed that there was a problem with the method of authentication in the httplib2 library. Possibly it was out of date as apparently this API is a bit of a moving target still these days. As a result, I had a look at the Google Account Authentication docs to see if I would have better luck doing the authentication manually. After a bit of fiddling, I ended up with this:

#!/usr/bin/env python

import httplib2
import re

def postEntry(auth):

    entry = """
    <?xml version="1.0" ?>
    <entry xmlns='http://www.w3.org/2005/Atom'>
      <title type='text'>Test Post from Python</title>
      <content type='xhtml'>
        <div xmlns="http://www.w3.org/1999/xhtml">
          <p>If you can see this, then it worked!</p>
        </div>
      </content>
      <author>
        <name>Dennis</name>
      </author>
    </entry>
    """

    headers = {'Content-Type': 'application/atom+xml', 'Authorization': 'GoogleLogin Auth=%s' % auth.strip()}
    response, content = h.request(uri, 'POST', body=entry, headers=headers)
    print response, content

def authenticate():
    auth_uri = 'https://www.google.com/accounts/ClientLogin'
    headers = {'Content-Type': 'application/x-www-form-urlencoded'}
    myrequest = "Email=MY_GMAIL_ACCOUNT&Passwd=MY_GMAIL_PASSWORD&service=blogger&service=Dcraven-TestApp-0.0"
    response, content = h.request(auth_uri, 'POST', body=myrequest, headers=headers)
    if response['status'] == '200':
        return re.search('Auth=(\S*)', content).group(1)
    else:
        return None

h = httplib2.Http()
h.follow_all_redirects = True
uri = 'http://www.blogger.com/feeds/BLOGID/posts/full'

cert = authenticate()
if cert:
    postEntry(cert)

The result of this was exactly the same thing, although the actual authenticate() function worked fine, so the problem doesn't lie there. The response from the POST call in this method was in fact 200, and I did get an Auth code from the server. Even after manually packing this code into the header as specified by the Google documentation, I still got an Error 401: Missing auth parameter in GoogleLogin returned by the postEntry() function.

I think this code should work, but alas, it does not. If anyone has any ideas why, or can spot an error in the code I'd really appreciate you pointing it out in a comment below. In the meantime I think I'll make posts the old fashioned way until I get the ambition to try again.

NOTE: I suppose I should note that in the above code, the words BLOGID, MY_GMAIL_PASSWORD, and MY_GMAIL_ACCOUNT actually contained the appropriate values when I tried to run the program :)

UPDATE: I just wanted to post this update for anyone landing here from a Google search or something. I ended up having success with this as posted here.

VimPastebin - Post to Pastebin from within Vim

The VimPastebin script is a Vim plugin script that posts selected text to the Pastebin of your choice and optionally sends the URL to your pasted text as Gajim message to someone in your roster. The syntax is chosen from the current filetype in the Vim editor. For example, if you are editing a Python file, the text sent to Pastebin will automatically be highlighted with Python syntax.

Requirements

This script is mostly written in Python, so it will only work if your Vim was compiled with the --enable-pythoninterp=yes configure option. To see if your installed Vim is compiled with this option, simply type the :version command inside Vim and look for +python. If you see -python in the list, this script will not work, otherwise you're golden!

Installation

Drop this script into your ~/.vim/plugin directory or somewhere else in your $VIMRUNTIME path. It will be sourced automatically next time you start Vim. You can source it manually by typing :source /path/to/script if you like.

There are a couple of configuration options in the script that should be modified for your particular settings. You can set your preferred Pastebin on line 38, and your preferred username on line 40. By default, the script included on this wiki page will post to http://pastebin.com as the user "vimuser".

Usage

To use the script is very simple. Just visually block (Shift-v) the lines of code you want to send to the Pastebin and enter the command :PasteCode. The blocked text will be sent to the Pastebin and the new URL will be shown on the Vim statusline. Optionally, you could enter the command :PasteGajim somename@somejid.org to send the new URL to the JID somename@somejid.org providing that JID appears in your roster. To make it even easier, you can send it to someone in your roster by only specifying their name, like :PasteGajim dcraven will send it to user name dcraven. This is handy if you cannot remember the person's full JID.

That's It!

That's pretty much all there is to it. If you missed the link to the actual script above, you can download it here. Enjoy!

Here's the code if you just want to have a quick look:

" Make sure the Vim was compiled with +python before loading the script...
if !has("python")
        finish
endif

" Map a keystroke for Visual Mode only (default:F2)
:vmap <f2> :PasteCode<cr>

" Define two commands.. One for just pastebinning alone, and another for
" Gajiming the results
:command -range -nargs=* PasteCode :call PasteMe(<line1>,<line2>,"None")
:command -range -nargs=* PasteGajim :call PasteMe(<line1>,<line2>,"<args>")

function! PasteMe(line1,line2,args)
python << EOF
import vim

line1 = vim.eval('a:line1')
line2 = vim.eval('a:line2')
jid = vim.eval('a:args')
format = vim.eval('&ft')

url = PBSend(line1, line2, format)
if not jid == "None":
    gajim_send_url(jid, url)
print "Pasted at %s" % url
EOF
endfunction

python << EOF
import vim
from urllib2 import urlopen
from re import compile

def PBSend(line1, line2, format='text'):
################### BEGIN USER CONFIG ###################
    # Set this to your preferred pastebin
    pastebin = 'http://pastebin.com'
    # Set this to your preferred username
    user = 'vimuser'
#################### END USER CONFIG ####################

    supported_formats = {
        'text' : 'text', 'bash' : 'bash', 'python' : 'python', 'c' : 'c',
        'cpp' : 'cpp', 'html' : 'html4strict', 'java' : 'java',
        'javascript' : 'javascript', 'perl' : 'perl', 'php' : 'php',
        'sql' : 'sql', 'ada' : 'ada', 'apache' : 'apache', 'asm' : 'asm',
        'aspvbs' : 'asp', 'dcl' : 'caddcl', 'lisp' : 'lisp', 'cs' : 'csharp',
        'css' : 'css', 'lua' : 'lua', 'masm' : 'mpasm', 'nsis' : 'nsis',
        'objc' : 'objc', 'ora' : 'oracle8', 'pascal' : 'pascal',
        'basic' : 'qbasic', 'smarty' : 'smarty', 'vb' : 'vb', 'xml' : 'xml'
    }

    if not (pastebin[:7] == 'http://'):
        pastebin = 'http://' + pastebin

    code = '\n'.join(vim.current.buffer[int(line1)-1:int(line2)])
    if format in supported_formats.keys():
        data = "format=%s&paste=Submit&code2=%s&poster=%s" % \
                (supported_formats[format], urlencode(code),
                urlencode(user))
    else:
        data = "format=text&paste=Submit&code2=%s&poster=%s" % \
                (urlencode(code), urlencode(user))

    u = urlopen(pastebin, data)
    f = "".join(u.readlines())
    rx = compile("<a.*?/(\d+)\">")
    ar = rx.findall(f)
    if len(ar) > 0:
        url = pastebin + '/' + ar[0]
        return url
    else:
        print 'An error occured.'

def gajim_send_url(jid, url):
    try:
        import dbus
        sbus = dbus.SessionBus()
        obj = sbus.get_object('org.gajim.dbus', '/org/gajim/dbus/RemoteObject')
        interface = dbus.Interface(obj, 'org.gajim.dbus.RemoteInterface')

        # Try to get the actual JID from your contact list
        list = interface.__getattr__('list_contacts')
        roster = list()
        for contact in roster:
            if jid.lower() == contact['name'].lower():
                jid = contact['jid']

        send = interface.__getattr__('send_message')
        if not send(jid, url):
            print "%s not found in Gajim roster." % jid
    except:
        print "Error sending message to %s." % jid

def urlencode(data):
    out = ""
    for char in data:
        if char.isalnum() or char in ['-','_']:
            out += char
        else:
            char = hex(ord(char))[2:]
            if len(char) == 1:
                char = "0" + char
            out += "%" + char
    return out
EOF

Saturday, October 07, 2006

Screw it... Let someone else host my crud :)

I've decided to migrate my own blog from my home server to here to see how it works out. I hardly ever make posts, and my hardware is unreliable. If I put it here, then maybe I can forget about it altogether. The hard/painful part is migrating all (3?) of my posts from my old home to here. heh