milton / metaweblog.py

# http://www.allyourpixel.com/post/metaweblog-38-django/

import urlparse
from django.conf import settings
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from milton.models import *
from milton.xmlrpc import public
from tagging.models import Tag
from tagging.utils import parse_tag_input
import xmlrpclib # import DateTime

def authenticated(pos=1):
    """
    A decorator for functions that require authentication.
    Assumes that the username & password are the second & third parameters.
    Doesn't perform real authorization (yet), it just checks that the
    user is_superuser.
    """
    
    def _decorate(func):
        def _wrapper(*args, **kwargs):
            username = args[pos+0]
            password = args[pos+1]
            args = args[0:pos]+args[pos+2:]
            try:
                user = User.objects.get(username__exact=username)
            except User.DoesNotExist:
                raise ValueError("Authentication Failure")
            if not user.check_password(password):
                raise ValueError("Authentication Failure")
            if not user.is_superuser:
                raise ValueError("Authorization Failure")
            return func(user, *args, **kwargs)
        
        return _wrapper
    return _decorate

def full_url(url=''):
    return urlparse.urljoin(str(MiltonSite.objects.get_current().base_url, url)

# example... this is what wordpress returns:
# {'permaLink': 'http://gabbas.wordpress.com/2006/05/09/hello-world/',
#  'description': 'Welcome to <a href="http://wordpress.com/">Wordpress.com</a>. This is your first post. Edit or delete it and start blogging!',
#  'title': 'Hello world!',
#  'mt_excerpt': '',
#  'userid': '217209',
#  'dateCreated': <DateTime u'20060509T16:24:39' at 2c7580>,
#  'link': 'http://gabbas.wordpress.com/2006/05/09/hello-world/',
#  'mt_text_more': '',
#  'mt_allow_comments': 1,
#  'postid': '1',
#  'categories': ['Uncategorized'],
#  'mt_allow_pings': 1}

def format_date(d):
    if not d: return None
    return xmlrpclib.DateTime(d.isoformat())

def post_struct(post):
    link = full_url(post.get_absolute_url())
    struct = {
        'postid': post.id,
        'title': post.title,
        'mt_basename': post.slug,
        'link': link,
        'permaLink': link,
        'description': post.teaser,
        'mt_tags': post.tags,
        'userid': post.user.id,
        # 'mt_excerpt': post.teaser,
        'mt_text_more': post.content,
        # 'mt_allow_comments': 1,
        # 'mt_allow_pings': 1
        'mt_convert_breaks': str(post.content_format), #Sadly, we have to use one format for both components.
        }
    if post.date_published:
        struct['dateCreated'] = format_date(post.date_published)
    
    return struct

def tag_struct(tag):
    struct = {
        'categoryId': tag.id,
        'categoryName': tag.name,
    }
    return struct

def _update_post(post, struct):
    if struct.get('dateCreated', None):
        post.date_published = struct['dateCreated']
    if struct.has_key('title'):
        post.title = struct['title']
    if struct.has_key('description'):
        post.teaser = struct['description']
    if struct.has_key('mt_text_more'):
        post.content = struct['mt_text_more']
    if struct.has_key('mt_tags'):
        post.tags = struct['mt_tags']
    elif struct.has_key('mt_keywords'):
        post.tags = struct('mt_keywords')
    if struct.has_key('mt_basename'):
        post.slug = struct['mt_basename']

''' Blogger API V1 '''

@public
@authenticated(pos=2)
def blogger_newPost(user, appkey, blogid, content, publish):
    pass

@public
@authenticated(pos=2)
def blogger_editPost(user, appkey, postid, content, publish):
    pass

@public
@authenticated(pos=2)
def blogger_deletePost(user, appkey, postid, publish):
    post = Story.objects.get(id=postid)
    post.delete()
    return True

@public
@authenticated()
def blogger_getUsersBlogs(user, appkey):
    """
    an array of <struct>'s containing the ID (blogid), name
    (blogName), and URL (url) of each blog.
    """
    print "entered"
    sections = Section.objects.all()
    print "got sections"
    result = [{
            'blogid': section.slug,
            'blogName': section.name,
            'url': full_url(reverse('story-archive', kwargs={'section':section.slug}))
            } for section in sections]
    print "results:", result
    return result

@public
@authenticated()
def blogger_getUserInfo(user, appkey):
    return {
        'nickname':user.username,
        'userid':user.id,
        'url':full_url(),
        'email':user.email,
        'lastname':user.last_name,
        'firstname':user.first_name,
        }

@public
@authenticated()
def metaWeblog_getCategories(user, blogid):
    tags = Tag.objects.all()
    result = [{'description':tag.name} for tag in tags]
    return result

@public
@authenticated()
def metaWeblog_getPost(user, postid):
    post = Story.objects.get(id=postid)
    return post_struct(post)

@public
@authenticated()
def metaWeblog_getRecentPosts(user, blogid, num_posts):
    posts = Story.objects.filter(section__name__exact=blogid).order_by('-date_published')[:int(num_posts)]
    return [post_struct(post) for post in posts]

@public
@authenticated()
def metaWeblog_newPost(user, blogid, struct, publish):
    post = Story(status = publish)
    post.user = user
    _update_post(post, struct)
    section = Section.objects.get(name__exact=blogid)
    post.section = blogid
    post.save()
    return post.id

@public
@authenticated()
def metaWeblog_editPost(user, postid, struct, publish):
    post = Story.objects.get(id=postid)
    post.status = publish
    _update_post(post, struct)
    post.save()
    return True

# http://qoli.de/blog/2007/nov/19/implementing-metaweblog-api/
@public
@authenticated()
def metaWeblog_newMediaObject(user, blogid, struct):        
    bits = b64decode(struct['bits'])
    name = struct['name']
    mime = struct['type']

    attachment= Attachment(content= bits,
                   filename= name,
                   contenttype= mime)
    attachment.save_content_file(name, bits)
    attachment.save()

    return {'url': attachment.get_content_url()}

@public
@authenticated()
def mt_getPostCategories(user, postid):
    post = Story.objects.get(id=int(postid))
    if (post.tags):
        tags = [tag_struct(t) for t in Tag.objects.filter(name__in=parse_tag_input(post.tags))]
    else:
        tags = []
    return tags

@public
@authenticated()
def mt_getCategoryList(user, blogid):
    return [tag_struct(t) for t in Tag.objects.all()]

@public
def mt_supportedTextFilters():
    return [{'label': f[1], 'key': str(f[0])} for f in CONTENT_FORMATTERS]
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.