summaryrefslogtreecommitdiff
path: root/gstudio/xmlrpc
diff options
context:
space:
mode:
authorgnowgi <nagarjun@gnowledge.org>2012-03-15 16:19:20 +0530
committergnowgi <nagarjun@gnowledge.org>2012-03-15 16:19:20 +0530
commit7a4f561e851fdc7246d804c3abb6748b8a4199a6 (patch)
treed2afc3463fd49625a9be482012f5c3bfcf7c42b9 /gstudio/xmlrpc
downloadgnowsys-7a4f561e851fdc7246d804c3abb6748b8a4199a6.tar.gz
master trunk of gnowsys-studio
Diffstat (limited to 'gstudio/xmlrpc')
-rw-r--r--gstudio/xmlrpc/__init__.py81
-rw-r--r--gstudio/xmlrpc/metaweblog.py344
-rw-r--r--gstudio/xmlrpc/pingback.py140
3 files changed, 565 insertions, 0 deletions
diff --git a/gstudio/xmlrpc/__init__.py b/gstudio/xmlrpc/__init__.py
new file mode 100644
index 0000000..88ced6b
--- /dev/null
+++ b/gstudio/xmlrpc/__init__.py
@@ -0,0 +1,81 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""XML-RPC methods for Gstudio"""
+
+
+GSTUDIO_XMLRPC_PINGBACK = [
+ ('gstudio.xmlrpc.pingback.pingback_ping',
+ 'pingback.ping'),
+ ('gstudio.xmlrpc.pingback.pingback_extensions_get_pingbacks',
+ 'pingback.extensions.getPingbacks')]
+
+GSTUDIO_XMLRPC_METAWEBLOG = [
+ ('gstudio.xmlrpc.metaweblog.get_users_blogs',
+ 'blogger.getUsersBlogs'),
+ ('gstudio.xmlrpc.metaweblog.get_user_info',
+ 'blogger.getUserInfo'),
+ ('gstudio.xmlrpc.metaweblog.delete_post',
+ 'blogger.deletePost'),
+ ('gstudio.xmlrpc.metaweblog.get_authors',
+ 'wp.getAuthors'),
+ ('gstudio.xmlrpc.metaweblog.get_metatypes',
+ 'metaWeblog.getMetatypes'),
+ ('gstudio.xmlrpc.metaweblog.new_metatype',
+ 'wp.newMetatype'),
+ ('gstudio.xmlrpc.metaweblog.get_recent_posts',
+ 'metaWeblog.getRecentPosts'),
+ ('gstudio.xmlrpc.metaweblog.get_post',
+ 'metaWeblog.getPost'),
+ ('gstudio.xmlrpc.metaweblog.new_post',
+ 'metaWeblog.newPost'),
+ ('gstudio.xmlrpc.metaweblog.edit_post',
+ 'metaWeblog.editPost'),
+ ('gstudio.xmlrpc.metaweblog.new_media_object',
+ 'metaWeblog.newMediaObject')]
+
+GSTUDIO_XMLRPC_METHODS = GSTUDIO_XMLRPC_PINGBACK + GSTUDIO_XMLRPC_METAWEBLOG
diff --git a/gstudio/xmlrpc/metaweblog.py b/gstudio/xmlrpc/metaweblog.py
new file mode 100644
index 0000000..56ee26c
--- /dev/null
+++ b/gstudio/xmlrpc/metaweblog.py
@@ -0,0 +1,344 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+"""XML-RPC methods of Gstudio metaWeblog API"""
+import os
+from datetime import datetime
+from xmlrpclib import Fault
+from xmlrpclib import DateTime
+
+from django.conf import settings
+from django.contrib.auth.models import User
+from django.contrib.sites.models import Site
+from django.core.urlresolvers import reverse
+from django.utils.translation import gettext as _
+from django.utils.html import strip_tags
+from django.utils.text import truncate_words
+from django.core.files.base import ContentFile
+from django.core.files.storage import default_storage
+from django.template.defaultfilters import slugify
+
+from gstudio.models import Nodetype
+from gstudio.models import Metatype
+from gstudio.settings import PROTOCOL
+from gstudio.settings import UPLOAD_TO
+from gstudio.managers import DRAFT, PUBLISHED
+from django_xmlrpc.decorators import xmlrpc_func
+
+# http://docs.nucleuscms.org/blog/12#errorcodes
+LOGIN_ERROR = 801
+PERMISSION_DENIED = 803
+
+
+def authenticate(username, password, permission=None):
+ """Authenticate staff_user with permission"""
+ try:
+ user = User.objects.get(username__exact=username)
+ except User.DoesNotExist:
+ raise Fault(LOGIN_ERROR, _('Username is incorrect.'))
+ if not user.check_password(password):
+ raise Fault(LOGIN_ERROR, _('Password is invalid.'))
+ if not user.is_staff or not user.is_active:
+ raise Fault(PERMISSION_DENIED, _('User account unavailable.'))
+ if permission:
+ if not user.has_perm(permission):
+ raise Fault(PERMISSION_DENIED, _('User cannot %s.') % permission)
+ return user
+
+
+def blog_structure(site):
+ """A blog structure"""
+ return {'url': '%s://%s%s' % (
+ PROTOCOL, site.domain, reverse('gstudio_nodetype_archive_index')),
+ 'blogid': settings.SITE_ID,
+ 'blogName': site.name}
+
+
+def user_structure(user, site):
+ """An user structure"""
+ return {'userid': user.pk,
+ 'email': user.email,
+ 'nickname': user.username,
+ 'lastname': user.last_name,
+ 'firstname': user.first_name,
+ 'url': '%s://%s%s' % (
+ PROTOCOL, site.domain,
+ reverse('gstudio_author_detail', args=[user.username]))}
+
+
+def author_structure(user):
+ """An author structure"""
+ return {'user_id': user.pk,
+ 'user_login': user.username,
+ 'display_name': user.username,
+ 'user_email': user.email}
+
+
+def metatype_structure(metatype, site):
+ """A metatype structure"""
+ return {'description': metatype.title,
+ 'htmlUrl': '%s://%s%s' % (
+ PROTOCOL, site.domain,
+ metatype.get_absolute_url()),
+ 'rssUrl': '%s://%s%s' % (
+ PROTOCOL, site.domain,
+ reverse('gstudio_metatype_feed', args=[metatype.tree_path])),
+ # Useful Wordpress Extensions
+ 'metatypeId': metatype.pk,
+ 'parentId': metatype.parent and metatype.parent.pk or 0,
+ 'metatypeDescription': metatype.description,
+ 'metatypeName': metatype.title}
+
+
+def post_structure(nodetype, site):
+ """A post structure with extensions"""
+ author = nodetype.authors.all()[0]
+ return {'title': nodetype.title,
+ 'description': unicode(nodetype.html_content),
+ 'link': '%s://%s%s' % (PROTOCOL, site.domain,
+ nodetype.get_absolute_url()),
+ # Basic Extensions
+ 'permaLink': '%s://%s%s' % (PROTOCOL, site.domain,
+ nodetype.get_absolute_url()),
+ 'metatypes': [cat.title for cat in nodetype.metatypes.all()],
+ 'dateCreated': DateTime(nodetype.creation_date.isoformat()),
+ 'postid': nodetype.pk,
+ 'userid': author.username,
+ # Useful Movable Type Extensions
+ 'mt_excerpt': nodetype.excerpt,
+ 'mt_allow_comments': int(nodetype.comment_enabled),
+ 'mt_allow_pings': int(nodetype.pingback_enabled),
+ 'mt_keywords': nodetype.tags,
+ # Useful Wordpress Extensions
+ 'wp_author': author.username,
+ 'wp_author_id': author.pk,
+ 'wp_author_display_name': author.username,
+ 'wp_password': nodetype.password,
+ 'wp_slug': nodetype.slug,
+ 'sticky': nodetype.featured}
+
+
+@xmlrpc_func(returns='struct[]', args=['string', 'string', 'string'])
+def get_users_blogs(apikey, username, password):
+ """blogger.getUsersBlogs(api_key, username, password)
+ => blog structure[]"""
+ authenticate(username, password)
+ site = Site.objects.get_current()
+ return [blog_structure(site)]
+
+
+@xmlrpc_func(returns='struct', args=['string', 'string', 'string'])
+def get_user_info(apikey, username, password):
+ """blogger.getUserInfo(api_key, username, password)
+ => user structure"""
+ user = authenticate(username, password)
+ site = Site.objects.get_current()
+ return user_structure(user, site)
+
+
+@xmlrpc_func(returns='struct[]', args=['string', 'string', 'string'])
+def get_authors(apikey, username, password):
+ """wp.getAuthors(api_key, username, password)
+ => author structure[]"""
+ authenticate(username, password)
+ return [author_structure(author)
+ for author in User.objects.filter(is_staff=True)]
+
+
+@xmlrpc_func(returns='boolean', args=['string', 'string',
+ 'string', 'string', 'string'])
+def delete_post(apikey, post_id, username, password, publish):
+ """blogger.deletePost(api_key, post_id, username, password, 'publish')
+ => boolean"""
+ user = authenticate(username, password, 'gstudio.delete_nodetype')
+ nodetype = Nodetype.objects.get(id=post_id, authors=user)
+ nodetype.delete()
+ return True
+
+
+@xmlrpc_func(returns='struct', args=['string', 'string', 'string'])
+def get_post(post_id, username, password):
+ """metaWeblog.getPost(post_id, username, password)
+ => post structure"""
+ user = authenticate(username, password)
+ site = Site.objects.get_current()
+ return post_structure(Nodetype.objects.get(id=post_id, authors=user), site)
+
+
+@xmlrpc_func(returns='struct[]',
+ args=['string', 'string', 'string', 'integer'])
+def get_recent_posts(blog_id, username, password, number):
+ """metaWeblog.getRecentPosts(blog_id, username, password, number)
+ => post structure[]"""
+ user = authenticate(username, password)
+ site = Site.objects.get_current()
+ return [post_structure(nodetype, site) \
+ for nodetype in Nodetype.objects.filter(authors=user)[:number]]
+
+
+@xmlrpc_func(returns='struct[]', args=['string', 'string', 'string'])
+def get_metatypes(blog_id, username, password):
+ """metaWeblog.getMetatypes(blog_id, username, password)
+ => metatype structure[]"""
+ authenticate(username, password)
+ site = Site.objects.get_current()
+ return [metatype_structure(metatype, site) \
+ for metatype in Metatype.objects.all()]
+
+
+@xmlrpc_func(returns='string', args=['string', 'string', 'string', 'struct'])
+def new_metatype(blog_id, username, password, metatype_struct):
+ """wp.newMetatype(blog_id, username, password, metatype)
+ => metatype_id"""
+ authenticate(username, password, 'gstudio.add_metatype')
+ metatype_dict = {'title': metatype_struct['name'],
+ 'description': metatype_struct['description'],
+ 'slug': metatype_struct['slug']}
+ if int(metatype_struct['parent_id']):
+ metatype_dict['parent'] = Metatype.objects.get(
+ pk=metatype_struct['parent_id'])
+ metatype = Metatype.objects.create(**metatype_dict)
+
+ return metatype.pk
+
+
+@xmlrpc_func(returns='string', args=['string', 'string', 'string',
+ 'struct', 'boolean'])
+def new_post(blog_id, username, password, post, publish):
+ """metaWeblog.newPost(blog_id, username, password, post, publish)
+ => post_id"""
+ user = authenticate(username, password, 'gstudio.add_nodetype')
+ if post.get('dateCreated'):
+ creation_date = datetime.strptime(
+ post['dateCreated'].value.replace('Z', '').replace('-', ''),
+ '%Y%m%dT%H:%M:%S')
+ else:
+ creation_date = datetime.now()
+
+ nodetype_dict = {'title': post['title'],
+ 'content': post['description'],
+ 'excerpt': post.get('mt_excerpt', truncate_words(
+ strip_tags(post['description']), 50)),
+ 'creation_date': creation_date,
+ 'last_update': creation_date,
+ 'comment_enabled': post.get('mt_allow_comments', 1) == 1,
+ 'pingback_enabled': post.get('mt_allow_pings', 1) == 1,
+ 'featured': post.get('sticky', 0) == 1,
+ 'tags': 'mt_keywords' in post and post['mt_keywords'] or '',
+ 'slug': 'wp_slug' in post and post['wp_slug'] or slugify(
+ post['title']),
+ 'password': post.get('wp_password', ''),
+ 'status': publish and PUBLISHED or DRAFT}
+ nodetype = Nodetype.objects.create(**nodetype_dict)
+
+ author = user
+ if 'wp_author_id' in post and user.has_perm('gstudio.can_change_author'):
+ if int(post['wp_author_id']) != user.pk:
+ author = User.objects.get(pk=post['wp_author_id'])
+ nodetype.authors.add(author)
+
+ nodetype.sites.add(Site.objects.get_current())
+ if 'metatypes' in post:
+ nodetype.metatypes.add(*[Metatype.objects.get_or_create(
+ title=cat, slug=slugify(cat))[0]
+ for cat in post['metatypes']])
+
+ return nodetype.pk
+
+
+@xmlrpc_func(returns='boolean', args=['string', 'string', 'string',
+ 'struct', 'boolean'])
+def edit_post(post_id, username, password, post, publish):
+ """metaWeblog.editPost(post_id, username, password, post, publish)
+ => boolean"""
+ user = authenticate(username, password, 'gstudio.change_nodetype')
+ nodetype = Nodetype.objects.get(id=post_id, authors=user)
+ if post.get('dateCreated'):
+ creation_date = datetime.strptime(
+ post['dateCreated'].value.replace('Z', '').replace('-', ''),
+ '%Y%m%dT%H:%M:%S')
+ else:
+ creation_date = nodetype.creation_date
+
+ nodetype.title = post['title']
+ nodetype.content = post['description']
+ nodetype.excerpt = post.get('mt_excerpt', truncate_words(
+ strip_tags(post['description']), 50))
+ nodetype.creation_date = creation_date
+ nodetype.last_update = datetime.now()
+ nodetype.comment_enabled = post.get('mt_allow_comments', 1) == 1
+ nodetype.pingback_enabled = post.get('mt_allow_pings', 1) == 1
+ nodetype.featured = post.get('sticky', 0) == 1
+ nodetype.tags = 'mt_keywords' in post and post['mt_keywords'] or ''
+ nodetype.slug = 'wp_slug' in post and post['wp_slug'] or slugify(
+ post['title'])
+ nodetype.status = publish and PUBLISHED or DRAFT
+ nodetype.password = post.get('wp_password', '')
+ nodetype.save()
+
+ if 'wp_author_id' in post and user.has_perm('gstudio.can_change_author'):
+ if int(post['wp_author_id']) != user.pk:
+ author = User.objects.get(pk=post['wp_author_id'])
+ nodetype.authors.clear()
+ nodetype.authors.add(author)
+
+ if 'metatypes' in post:
+ nodetype.metatypes.clear()
+ nodetype.metatypes.add(*[Metatype.objects.get_or_create(
+ title=cat, slug=slugify(cat))[0]
+ for cat in post['metatypes']])
+ return True
+
+
+@xmlrpc_func(returns='struct', args=['string', 'string', 'string', 'struct'])
+def new_media_object(blog_id, username, password, media):
+ """metaWeblog.newMediaObject(blog_id, username, password, media)
+ => media structure"""
+ authenticate(username, password)
+ path = default_storage.save(os.path.join(UPLOAD_TO, media['name']),
+ ContentFile(media['bits'].data))
+ return {'url': default_storage.url(path)}
diff --git a/gstudio/xmlrpc/pingback.py b/gstudio/xmlrpc/pingback.py
new file mode 100644
index 0000000..44aaafd
--- /dev/null
+++ b/gstudio/xmlrpc/pingback.py
@@ -0,0 +1,140 @@
+"""XML-RPC methods of Gstudio Pingback"""
+from urllib2 import urlopen
+from urllib2 import URLError
+from urllib2 import HTTPError
+from urlparse import urlsplit
+
+from django.contrib import comments
+from django.utils.html import strip_tags
+from django.contrib.sites.models import Site
+from django.core.urlresolvers import resolve
+from django.core.urlresolvers import Resolver404
+from django.utils.translation import ugettext as _
+from django.contrib.contenttypes.models import ContentType
+
+from gstudio.models import Nodetype
+from gstudio.settings import PINGBACK_CONTENT_LENGTH
+from BeautifulSoup import BeautifulSoup
+from django_xmlrpc.decorators import xmlrpc_func
+
+UNDEFINED_ERROR = 0
+SOURCE_DOES_NOT_EXIST = 16
+SOURCE_DOES_NOT_LINK = 17
+TARGET_DOES_NOT_EXIST = 32
+TARGET_IS_NOT_PINGABLE = 33
+PINGBACK_ALREADY_REGISTERED = 48
+
+
+def generate_pingback_content(soup, target, max_length, trunc_char='...'):
+ """Generate a description text for the pingback"""
+ link = soup.find('a', href=target)
+
+ content = strip_tags(unicode(link.findParent()))
+ index = content.index(link.string)
+
+ if len(content) > max_length:
+ middle = max_length / 2
+ start = index - middle
+ end = index + middle
+
+ if start <= 0:
+ end -= start
+ extract = content[0:end]
+ else:
+ extract = '%s%s' % (trunc_char, content[start:end])
+
+ if end < len(content):
+ extract += trunc_char
+ return extract
+
+ return content
+
+
+@xmlrpc_func(returns='string', args=['string', 'string'])
+def pingback_ping(source, target):
+ """pingback.ping(sourceURI, targetURI) => 'Pingback message'
+
+ Notifies the server that a link has been added to sourceURI,
+ pointing to targetURI.
+
+ See: http://hixie.ch/specs/pingback/pingback-1.0"""
+ try:
+ if source == target:
+ return UNDEFINED_ERROR
+
+ site = Site.objects.get_current()
+ try:
+ document = ''.join(urlopen(source).readlines())
+ except (HTTPError, URLError):
+ return SOURCE_DOES_NOT_EXIST
+
+ if not target in document:
+ return SOURCE_DOES_NOT_LINK
+
+ scheme, netloc, path, query, fragment = urlsplit(target)
+ if netloc != site.domain:
+ return TARGET_DOES_NOT_EXIST
+
+ try:
+ view, args, kwargs = resolve(path)
+ except Resolver404:
+ return TARGET_DOES_NOT_EXIST
+
+ try:
+ nodetype = Nodetype.published.get(
+ slug=kwargs['slug'],
+ creation_date__year=kwargs['year'],
+ creation_date__month=kwargs['month'],
+ creation_date__day=kwargs['day'])
+ if not nodetype.pingback_enabled:
+ return TARGET_IS_NOT_PINGABLE
+ except (KeyError, Nodetype.DoesNotExist):
+ return TARGET_IS_NOT_PINGABLE
+
+ soup = BeautifulSoup(document)
+ title = soup.find('title')
+ title = title and strip_tags(title) or _('No title')
+ description = generate_pingback_content(soup, target,
+ PINGBACK_CONTENT_LENGTH)
+
+ comment, created = comments.get_model().objects.get_or_create(
+ content_type=ContentType.objects.get_for_model(Nodetype),
+ object_pk=nodetype.pk, user_url=source, site=site,
+ defaults={'comment': description, 'user_name': title})
+ if created:
+ user = nodetype.authors.all()[0]
+ comment.flags.create(user=user, flag='pingback')
+ return 'Pingback from %s to %s registered.' % (source, target)
+ return PINGBACK_ALREADY_REGISTERED
+ except:
+ return UNDEFINED_ERROR
+
+
+@xmlrpc_func(returns='string[]', args=['string'])
+def pingback_extensions_get_pingbacks(target):
+ """pingback.extensions.getPingbacks(url) => '[url, url, ...]'
+
+ Returns an array of URLs that link to the specified url.
+
+ See: http://www.aquarionics.com/misc/archives/blogite/0198.html"""
+ site = Site.objects.get_current()
+
+ scheme, netloc, path, query, fragment = urlsplit(target)
+ if netloc != site.domain:
+ return TARGET_DOES_NOT_EXIST
+
+ try:
+ view, args, kwargs = resolve(path)
+ except Resolver404:
+ return TARGET_DOES_NOT_EXIST
+
+ try:
+ nodetype = Nodetype.published.get(
+ slug=kwargs['slug'],
+ creation_date__year=kwargs['year'],
+ creation_date__month=kwargs['month'],
+ creation_date__day=kwargs['day'])
+ except (KeyError, Nodetype.DoesNotExist):
+ return TARGET_IS_NOT_PINGABLE
+
+ return [pingback.user_url for pingback in nodetype.pingbacks]