zinnia.xmlrpc.pingback
Covered: 106 lines
Missed: 0 lines
Skipped 30 lines
Percent: 100 %
  1
"""XML-RPC methods of Zinnia Pingback"""
  2
from urllib2 import urlopen
  3
from urllib2 import URLError
  4
from urllib2 import HTTPError
  5
from urlparse import urlsplit
  7
from django.utils.html import strip_tags
  8
from django.contrib.sites.models import Site
  9
from django.core.urlresolvers import Resolver404
 10
from django.core.urlresolvers import get_resolver
 11
from django.contrib.comments.models import Comment
 12
from django.utils.translation import ugettext as _
 13
from django.contrib.contenttypes.models import ContentType
 15
from zinnia.models import Entry
 16
from zinnia.settings import PINGBACK_CONTENT_LENGTH
 17
from BeautifulSoup import BeautifulSoup
 18
from django_xmlrpc.decorators import xmlrpc_func
 20
UNDEFINED_ERROR = 0
 21
SOURCE_DOES_NOT_EXIST = 16
 22
SOURCE_DOES_NOT_LINK = 17
 23
TARGET_DOES_NOT_EXIST = 32
 24
TARGET_IS_NOT_PINGABLE = 33
 25
PINGBACK_ALREADY_REGISTERED = 48
 28
def generate_pingback_content(soup, target, max_length, trunc_char='...'):
 29
    """Generate a description text for the pingback"""
 30
    link = soup.find('a', href=target)
 32
    content = strip_tags(unicode(link.findParent()))
 33
    index = content.index(link.string)
 35
    if len(content) > max_length:
 36
        middle = max_length / 2
 37
        start = index - middle
 38
        end = index + middle
 40
        if start <= 0:
 41
            end -= start
 42
            extract = content[0:end]
 43
        else:
 44
            extract = '%s%s' % (trunc_char, content[start:end])
 46
        if end < len(content):
 47
            extract += trunc_char
 48
        return extract
 50
    return content
 53
@xmlrpc_func(returns='string', args=['string', 'string'])
 54
def pingback_ping(source, target):
 55
    """pingback.ping(sourceURI, targetURI) => 'Pingback message'
 57
    Notifies the server that a link has been added to sourceURI, pointing to targetURI.
 59
    See: http://hixie.ch/specs/pingback/pingback-1.0"""
 60
    try:
 61
        if source == target:
 62
            return UNDEFINED_ERROR
 64
        site = Site.objects.get_current()
 65
        try:
 66
            document = ''.join(urlopen(source).readlines())
 67
        except (HTTPError, URLError):
 68
            return SOURCE_DOES_NOT_EXIST
 70
        if not target in document:
 71
            return SOURCE_DOES_NOT_LINK
 73
        scheme, netloc, path, query, fragment = urlsplit(target)
 74
        if netloc != site.domain:
 75
            return TARGET_DOES_NOT_EXIST
 77
        resolver = get_resolver(None)
 78
        try:
 79
            resolver.resolve(path)
 80
        except Resolver404:
 81
            return TARGET_DOES_NOT_EXIST
 83
        try:
 84
            entry_slug = [bit for bit in path.split('/') if bit][-1]
 85
            entry = Entry.published.get(slug=entry_slug)
 86
            if not entry.pingback_enabled:
 87
                return TARGET_IS_NOT_PINGABLE
 88
        except (Entry.DoesNotExist, IndexError):
 89
            return TARGET_IS_NOT_PINGABLE
 91
        soup = BeautifulSoup(document)
 92
        title = soup.find('title')
 93
        title = title and strip_tags(title) or _('No title')
 94
        description = generate_pingback_content(soup, target,
 95
                                                PINGBACK_CONTENT_LENGTH)
 97
        comment, created = Comment.objects.get_or_create(
 98
            content_type=ContentType.objects.get_for_model(Entry),
 99
            object_pk=entry.pk, user_url=source, site=site,
100
            defaults={'comment': description, 'user_name': title})
101
        if created:
102
            user = entry.authors.all()[0]
103
            comment.flags.create(user=user, flag='pingback')
104
            return 'Pingback from %s to %s registered.' % (source, target)
105
        return PINGBACK_ALREADY_REGISTERED
106
    except:
107
        return UNDEFINED_ERROR
110
@xmlrpc_func(returns='string[]', args=['string'])
111
def pingback_extensions_get_pingbacks(target):
112
    """pingback.extensions.getPingbacks(url) => '[url, url, ...]'
114
    Returns an array of URLs that link to the specified url.
116
    See: http://www.aquarionics.com/misc/archives/blogite/0198.html"""
117
    site = Site.objects.get_current()
119
    scheme, netloc, path, query, fragment = urlsplit(target)
120
    if netloc != site.domain:
121
        return TARGET_DOES_NOT_EXIST
123
    resolver = get_resolver(None)
124
    try:
125
        resolver.resolve(path)
126
    except Resolver404:
127
        return TARGET_DOES_NOT_EXIST
129
    try:
130
        entry_slug = [bit for bit in path.split('/') if bit][-1]
131
        entry = Entry.published.get(slug=entry_slug)
132
    except (Entry.DoesNotExist, IndexError):
133
        return TARGET_IS_NOT_PINGABLE
135
    return [pingback.user_url for pingback in entry.pingbacks]