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
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
42
extract = content[0:end]
44
extract = '%s%s' % (trunc_char, content[start:end])
46
if end < len(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"""
62
return UNDEFINED_ERROR
64
site = Site.objects.get_current()
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)
79
resolver.resolve(path)
81
return TARGET_DOES_NOT_EXIST
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})
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
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)
125
resolver.resolve(path)
127
return TARGET_DOES_NOT_EXIST
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]