1
"""XML-RPC methods of Zinnia metaWeblog API"""
3
from datetime import datetime
4
from xmlrpclib import Fault
5
from xmlrpclib import DateTime
7
from django.conf import settings
8
from django.contrib.auth.models import User
9
from django.contrib.sites.models import Site
10
from django.core.urlresolvers import reverse
11
from django.utils.translation import gettext as _
12
from django.utils.html import strip_tags
13
from django.utils.text import truncate_words
14
from django.core.files.base import ContentFile
15
from django.core.files.storage import default_storage
16
from django.template.defaultfilters import slugify
18
from zinnia.models import Entry
19
from zinnia.models import Category
20
from zinnia.settings import PROTOCOL
21
from zinnia.settings import UPLOAD_TO
22
from zinnia.managers import DRAFT, PUBLISHED
23
from django_xmlrpc.decorators import xmlrpc_func
25
# http://docs.nucleuscms.org/blog/12#errorcodes
27
PERMISSION_DENIED = 803
30
def authenticate(username, password, permission=None):
31
"""Authenticate staff_user with permission"""
33
user = User.objects.get(username__exact=username)
34
except User.DoesNotExist:
35
raise Fault(LOGIN_ERROR, _('Username is incorrect.'))
36
if not user.check_password(password):
37
raise Fault(LOGIN_ERROR, _('Password is invalid.'))
38
if not user.is_staff or not user.is_active:
39
raise Fault(PERMISSION_DENIED, _('User account unavailable.'))
41
if not user.has_perm(permission):
42
raise Fault(PERMISSION_DENIED, _('User cannot %s.') % permission)
46
def blog_structure(site):
47
"""A blog structure"""
48
return {'url': '%s://%s%s' % (
49
PROTOCOL, site.domain, reverse('zinnia_entry_archive_index')),
50
'blogid': settings.SITE_ID,
51
'blogName': site.name}
54
def user_structure(user, site):
55
"""An user structure"""
56
return {'userid': user.pk,
58
'nickname': user.username,
59
'lastname': user.last_name,
60
'firstname': user.first_name,
61
'url': '%s://%s%s' % (
62
PROTOCOL, site.domain,
63
reverse('zinnia_author_detail', args=[user.username]))}
66
def author_structure(user):
67
"""An author structure"""
68
return {'user_id': user.pk,
69
'user_login': user.username,
70
'display_name': user.username,
71
'user_email': user.email}
74
def category_structure(category, site):
75
"""A category structure"""
76
return {'description': category.title,
77
'htmlUrl': '%s://%s%s' % (
78
PROTOCOL, site.domain,
79
category.get_absolute_url()),
80
'rssUrl': '%s://%s%s' % (
81
PROTOCOL, site.domain,
82
reverse('zinnia_category_feed', args=[category.tree_path])),
83
# Useful Wordpress Extensions
84
'categoryId': category.pk,
85
'parentId': category.parent and category.parent.pk or 0,
86
'categoryDescription': category.description,
87
'categoryName': category.title}
90
def post_structure(entry, site):
91
"""A post structure with extensions"""
92
author = entry.authors.all()[0]
93
return {'title': entry.title,
94
'description': unicode(entry.html_content),
95
'link': '%s://%s%s' % (PROTOCOL, site.domain,
96
entry.get_absolute_url()),
98
'permaLink': '%s://%s%s' % (PROTOCOL, site.domain,
99
entry.get_absolute_url()),
100
'categories': [cat.title for cat in entry.categories.all()],
101
'dateCreated': DateTime(entry.creation_date.isoformat()),
103
'userid': author.username,
104
# Useful Movable Type Extensions
105
'mt_excerpt': entry.excerpt,
106
'mt_allow_comments': int(entry.comment_enabled),
107
'mt_allow_pings': int(entry.pingback_enabled),
108
'mt_keywords': entry.tags,
109
# Useful Wordpress Extensions
110
'wp_author': author.username,
111
'wp_author_id': author.pk,
112
'wp_author_display_name': author.username,
113
'wp_password': entry.password,
114
'wp_slug': entry.slug,
115
'sticky': entry.featured}
118
@xmlrpc_func(returns='struct[]', args=['string', 'string', 'string'])
119
def get_users_blogs(apikey, username, password):
120
"""blogger.getUsersBlogs(api_key, username, password)
121
=> blog structure[]"""
122
authenticate(username, password)
123
site = Site.objects.get_current()
124
return [blog_structure(site)]
127
@xmlrpc_func(returns='struct', args=['string', 'string', 'string'])
128
def get_user_info(apikey, username, password):
129
"""blogger.getUserInfo(api_key, username, password)
131
user = authenticate(username, password)
132
site = Site.objects.get_current()
133
return user_structure(user, site)
136
@xmlrpc_func(returns='struct[]', args=['string', 'string', 'string'])
137
def get_authors(apikey, username, password):
138
"""wp.getAuthors(api_key, username, password)
139
=> author structure[]"""
140
authenticate(username, password)
141
return [author_structure(author)
142
for author in User.objects.filter(is_staff=True)]
145
@xmlrpc_func(returns='boolean', args=['string', 'string',
146
'string', 'string', 'string'])
147
def delete_post(apikey, post_id, username, password, publish):
148
"""blogger.deletePost(api_key, post_id, username, password, 'publish')
150
user = authenticate(username, password, 'zinnia.delete_entry')
151
entry = Entry.objects.get(id=post_id, authors=user)
156
@xmlrpc_func(returns='struct', args=['string', 'string', 'string'])
157
def get_post(post_id, username, password):
158
"""metaWeblog.getPost(post_id, username, password)
160
user = authenticate(username, password)
161
site = Site.objects.get_current()
162
return post_structure(Entry.objects.get(id=post_id, authors=user), site)
165
@xmlrpc_func(returns='struct[]', args=['string', 'string', 'string', 'integer'])
166
def get_recent_posts(blog_id, username, password, number):
167
"""metaWeblog.getRecentPosts(blog_id, username, password, number)
168
=> post structure[]"""
169
user = authenticate(username, password)
170
site = Site.objects.get_current()
171
return [post_structure(entry, site) \
172
for entry in Entry.objects.filter(authors=user)[:number]]
175
@xmlrpc_func(returns='struct[]', args=['string', 'string', 'string'])
176
def get_categories(blog_id, username, password):
177
"""metaWeblog.getCategories(blog_id, username, password)
178
=> category structure[]"""
179
authenticate(username, password)
180
site = Site.objects.get_current()
181
return [category_structure(category, site) \
182
for category in Category.objects.all()]
185
@xmlrpc_func(returns='string', args=['string', 'string', 'string', 'struct'])
186
def new_category(blog_id, username, password, category_struct):
187
"""wp.newCategory(blog_id, username, password, category)
189
authenticate(username, password, 'zinnia.add_category')
190
category_dict = {'title': category_struct['name'],
191
'description': category_struct['description'],
192
'slug': category_struct['slug']}
193
if int(category_struct['parent_id']):
194
category_dict['parent'] = Category.objects.get(
195
pk=category_struct['parent_id'])
196
category = Category.objects.create(**category_dict)
201
@xmlrpc_func(returns='string', args=['string', 'string', 'string',
202
'struct', 'boolean'])
203
def new_post(blog_id, username, password, post, publish):
204
"""metaWeblog.newPost(blog_id, username, password, post, publish)
206
user = authenticate(username, password, 'zinnia.add_entry')
207
if post.get('dateCreated'):
208
creation_date = datetime.strptime(
209
post['dateCreated'].value.replace('Z', '').replace('-', ''),
212
creation_date = datetime.now()
214
entry_dict = {'title': post['title'],
215
'content': post['description'],
216
'excerpt': post.get('mt_excerpt', truncate_words(
217
strip_tags(post['description']), 50)),
218
'creation_date': creation_date,
219
'last_update': creation_date,
220
'comment_enabled': post.get('mt_allow_comments', 1) == 1,
221
'pingback_enabled': post.get('mt_allow_pings', 1) == 1,
222
'featured': post.get('sticky', 0) == 1,
223
'tags': 'mt_keywords' in post and post['mt_keywords'] or '',
224
'slug': 'wp_slug' in post and post['wp_slug'] or slugify(
226
'password': post.get('wp_password', ''),
227
'status': publish and PUBLISHED or DRAFT}
228
entry = Entry.objects.create(**entry_dict)
231
if 'wp_author_id' in post and user.has_perm('zinnia.can_change_author'):
232
if int(post['wp_author_id']) != user.pk:
233
author = User.objects.get(pk=post['wp_author_id'])
234
entry.authors.add(author)
236
entry.sites.add(Site.objects.get_current())
237
if 'categories' in post:
238
entry.categories.add(*[Category.objects.get_or_create(
239
title=cat, slug=slugify(cat))[0]
240
for cat in post['categories']])
245
@xmlrpc_func(returns='boolean', args=['string', 'string', 'string',
246
'struct', 'boolean'])
247
def edit_post(post_id, username, password, post, publish):
248
"""metaWeblog.editPost(post_id, username, password, post, publish)
250
user = authenticate(username, password, 'zinnia.change_entry')
251
entry = Entry.objects.get(id=post_id, authors=user)
252
if post.get('dateCreated'):
253
creation_date = datetime.strptime(
254
post['dateCreated'].value.replace('Z', '').replace('-', ''),
257
creation_date = entry.creation_date
259
entry.title = post['title']
260
entry.content = post['description']
261
entry.excerpt = post.get('mt_excerpt', truncate_words(
262
strip_tags(post['description']), 50))
263
entry.creation_date = creation_date
264
entry.last_update = datetime.now()
265
entry.comment_enabled = post.get('mt_allow_comments', 1) == 1
266
entry.pingback_enabled = post.get('mt_allow_pings', 1) == 1
267
entry.featured = post.get('sticky', 0) == 1
268
entry.tags = 'mt_keywords' in post and post['mt_keywords'] or ''
269
entry.slug = 'wp_slug' in post and post['wp_slug'] or slugify(
271
entry.status = publish and PUBLISHED or DRAFT
272
entry.password = post.get('wp_password', '')
275
if 'wp_author_id' in post and user.has_perm('zinnia.can_change_author'):
276
if int(post['wp_author_id']) != user.pk:
277
author = User.objects.get(pk=post['wp_author_id'])
278
entry.authors.clear()
279
entry.authors.add(author)
281
if 'categories' in post:
282
entry.categories.clear()
283
entry.categories.add(*[Category.objects.get_or_create(
284
title=cat, slug=slugify(cat))[0]
285
for cat in post['categories']])
289
@xmlrpc_func(returns='struct', args=['string', 'string', 'string', 'struct'])
290
def new_media_object(blog_id, username, password, media):
291
"""metaWeblog.newMediaObject(blog_id, username, password, media)
292
=> media structure"""
293
authenticate(username, password)
294
path = default_storage.save(os.path.join(UPLOAD_TO, media['name']),
295
ContentFile(media['bits'].data))
296
return {'url': default_storage.url(path)}