# No shebang line. This module is meant to be imported.
#
# Copyright 2010. Luis Artola. All rights reserved.
#
#
# $URL: file:///svn/restblog/trunk/src/python/restblog/server.py $
# $Date: 2010-07-31 14:27:54 -0700 (Sat, 31 Jul 2010) $
# $Revision: 186 $
#
# History:
# 2010.06.27 lartola Initial working version
#
'''
Encapsulates a connection to a blog server and provides a simplified API to
manage posts and pages.
:copyright: Copyright 2010 Luis Artola.
:license: BSD, see LICENSE.txt for details.
'''
import getpass
import os
from xml.etree import ElementTree
import xmlrpclib
[docs]class Server( object ):
'''Server( url, user='', password='' ) -> instance
Provides a simplified API to a subset of methods from the various blogging
APIs, i.e. Wordpress, MetaWeblog, Blogger, MoveableType. Manages the
connection to a blog server using XMLRPC.
Parameters:
- url: Address of the XMLRPC blog server, e.g. http://your.blog.com/xmlrpc.php
- user: Optional user name.
- password: Optional password.
'''
def __init__( self, url, user='', password='' ):
self.__blog = xmlrpclib.Server( url )
self.__user = user if user else ''
self.__password = password if password else ''
self.__blog_id = 1
[docs] def getRecentPostTitles( self, count=10 ):
'''getRecentPostTitles( count=10 ) -> list
Returns a list with minimal information about the most recent posts.
Encapsulates MoveableType function ``mt.getRecentPostTitles``.
Parameters:
- count: Number of posts to retrieve. Default is 10.
Returns:
A list of dictionaries representing data from the post. Including
``postid``, ``title`` and ``dateCreated``.
'''
titles = self.__blog.mt.getRecentPostTitles(
self.__blog_id, self.__user, self.__password, count
)
return titles
[docs] def newPost( self, contents, publish ):
'''newPost( contents, publish ) -> int
Creates a new post.
Encapsulates MetaWeblog function ``metaWeblog.newPost``.
Parameters:
- contents: A dictionary with the keys listed below.
- title: str
- description: str
- mt_excerpt: str
- mt_text_more: str
- mt_keywords: list
- categories: list
- publish: Whether to publish immediately or not. Default is True.
Returns:
An integer with the post ID just created.
'''
id = self.__blog.metaWeblog.newPost(
self.__blog_id, self.__user, self.__password, contents, publish
)
return id
[docs] def editPost( self, id, contents, publish ):
'''edit( contents, publish ) -> bool
Updates the post with the given ``id`` and ``contents``.
Encapsulates MetaWeblog function ``metaWeblog.editPost``.
Parameters:
- contents: A dictionary with the keys listed below.
- title: str
- description: str
- mt_excerpt: str
- mt_text_more: str
- mt_keywords: list
- categories: list
- publish: Whether to publish changes immediately or not.
Default is True.
Returns:
True if the update was successful. False otherwise.
'''
success = self.__blog.metaWeblog.editPost(
id, self.__user, self.__password, contents, publish
)
return success
def getPost( self, id ):
'''getPost( id ) -> dict
Returns the post with the given ``id``.
Encapsulates MetaWeblog function ``metaWeblog.getPost``.
Parameters:
- id: Post ID to return.
Returns:
- A dictionary with the keys listed below.
- title: str
- description: str
- mt_excerpt: str
- mt_text_more: str
- mt_keywords: list
- categories: list
'''
post = self.__blog.metaWeblog.getPost(
id, self.__user, self.__password
)
return post
[docs] def deletePost( self, id, publish ):
'''deletePost( id, publish ) -> bool
Deletes post with the given ``id``.
Encapsulates MetaWeblog function ``metaWeblog.deletePost``.
Parameters:
- id: Post ID to delete.
- publish: Whether to publish the deletion immediately or not.
Default is True.
Returns:
True if the deletion was successful. False otherwise.
'''
success = self.__blog.metaWeblog.deletePost(
'', id, self.__user, self.__password, publish
)
return success
[docs] def newPage( self, contents, publish ):
'''newPage( contents, publish ) -> int
Creates a new page. This function is practically identical to
``newPost``. It receives the same parameters. One note though,
``contents`` may have some extra keys that do not quite apply
to pages, e.g. tags and categories, but are gracefully ignored
by the actual implementation on the server side.
Encapsulates Wordpress function ``wp.newPage``.
Parameters:
- contents: A dictionary with the keys described in ``newPost``.
- publish: Whether to publish immediately or not. Default is True.
Returns:
An integer with the page ID just created.
'''
id = self.__blog.wp.newPage(
self.__blog_id, self.__user, self.__password, contents, publish
)
return id
[docs] def editPage( self, id, contents, publish ):
'''editPage( id, contents, publish ) -> bool
Edit page with the given ``id``. This function is practically
identical to ``editPost``. It receives the same parameters.
One note though, ``contents`` may have some extra keys that do not
quite apply to pages, e.g. tags and categories, but are gracefully
ignored by the actual implementation on the server side.
Encapsulates Wordpress function ``wp.editPage``.
Parameters:
- contents: A dictionary with the keys described in ``editPost``.
- publish: Whether to publish immediately or not. Default is True.
Returns:
True if the update was successful. False otherwise.
'''
success = self.__blog.wp.editPage(
self.__blog_id, id, self.__user, self.__password, contents, publish
)
return success
def ping( self ):
'''ping()
Sends a request to the server just to verify that it can log in with
the credentials given when constructing the ``Server`` instance.
Raises an exception if the request fails for any reason, including
bad credentials or simply connection errors.
'''
# Verify that we can connect to the server and there are remote methods
# in the API that can be executed.
self.__blog.system.listMethods()
[docs]def connect( url='', user='', password='', interactive=False ):
'''connect( url='', user='', password='', interactive=False ) -> Server
Convenience function to create a ``Server`` object. This is the preferred
method over constructing a ``Server`` directly. It would attempt to use
the given credentials if not empty. Otherwise, it would look into the
*.restblog* directory, if it exists, to extract the default credentials.
Parameters:
- url: Address of the XMLRPC blog server, e.g. http://your.blog.com/xmlrpc.php
- user: Optional user name.
- password: Optional password.
- interactive: Prompts user for missing credentials like user and/or
password when set to True. By default it is set to False meaning that it
would use the given credentials and fail if it can't connect for any
reason.
Returns:
A ``Server`` instance.
'''
# Attempt to extract missing credentials from the .restblog
# directory first
file_name = os.path.join( os.getcwd(), '.restblog', 'restblog.xml' )
if not url and file_name:
document = ElementTree.parse( file_name )
restblog = document.getroot()
server = restblog.find( 'server' )
url = server.attrib.get( 'url', '' )
user = server.attrib.get( 'user', '' )
password = server.attrib.get( 'password', '' )
# We need a URL at the very least, fail if that's not the case
if not url:
raise RuntimeError, 'Please specify a blog URL'
# Fill in missing credentials if instructed to do so
if interactive:
if not user:
user = raw_input( 'User: ' )
if not password:
password = getpass.getpass( 'Password: ' )
# Attempt a server connection and verify that it works
try:
server = Server( url, user, password )
server.ping()
return server
except Exception, ex:
raise RuntimeError, 'Unable to open connection to %(url)s. Error: %(ex)s' % locals()