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
6
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
14
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
19
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
26
27
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)
31
32 content = strip_tags(unicode(link.findParent()))
33 index = content.index(link.string)
34
35 if len(content) > max_length:
36 middle = max_length / 2
37 start = index - middle
38 end = index + middle
39
40 if start <= 0:
41 end -= start
42 extract = content[0:end]
43 else:
44 extract = '%s%s' % (trunc_char, content[start:end])
45
46 if end < len(content):
47 extract += trunc_char
48 return extract
49
50 return content
51
52
53 @xmlrpc_func(returns='string', args=['string', 'string'])
54 -def pingback_ping(source, target):
55 """pingback.ping(sourceURI, targetURI) => 'Pingback message'
56
57 Notifies the server that a link has been added to sourceURI, pointing to targetURI.
58
59 See: http://hixie.ch/specs/pingback/pingback-1.0"""
60 try:
61 if source == target:
62 return UNDEFINED_ERROR
63
64 site = Site.objects.get_current()
65 try:
66 document = ''.join(urlopen(source).readlines())
67 except (HTTPError, URLError):
68 return SOURCE_DOES_NOT_EXIST
69
70 if not target in document:
71 return SOURCE_DOES_NOT_LINK
72
73 scheme, netloc, path, query, fragment = urlsplit(target)
74 if netloc != site.domain:
75 return TARGET_DOES_NOT_EXIST
76
77 resolver = get_resolver(None)
78 try:
79 resolver.resolve(path)
80 except Resolver404:
81 return TARGET_DOES_NOT_EXIST
82
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
90
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)
96
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
108
112 """pingback.extensions.getPingbacks(url) => '[url, url, ...]'
113
114 Returns an array of URLs that link to the specified url.
115
116 See: http://www.aquarionics.com/misc/archives/blogite/0198.html"""
117 site = Site.objects.get_current()
118
119 scheme, netloc, path, query, fragment = urlsplit(target)
120 if netloc != site.domain:
121 return TARGET_DOES_NOT_EXIST
122
123 resolver = get_resolver(None)
124 try:
125 resolver.resolve(path)
126 except Resolver404:
127 return TARGET_DOES_NOT_EXIST
128
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
134
135 return [pingback.user_url for pingback in entry.pingbacks]
136