1
"""EntryAdmin for Zinnia"""
2
from datetime import datetime
4
from django.forms import Media
5
from django.contrib import admin
6
from django.contrib.auth.models import User
7
from django.utils.html import strip_tags
8
from django.utils.text import truncate_words
9
from django.conf.urls.defaults import url
10
from django.conf.urls.defaults import patterns
11
from django.utils.translation import ugettext_lazy as _
12
from django.core.urlresolvers import reverse, NoReverseMatch
14
from tagging.models import Tag
16
from zinnia import settings
17
from zinnia.managers import HIDDEN
18
from zinnia.managers import PUBLISHED
19
from zinnia.ping import DirectoryPinger
20
from zinnia.admin.forms import EntryAdminForm
23
class EntryAdmin(admin.ModelAdmin):
24
"""Admin for Entry model"""
26
date_hierarchy = 'creation_date'
27
fieldsets = ((_('Content'), {'fields': ('title', 'content',
29
(_('Options'), {'fields': ('featured', 'excerpt', 'template',
31
'creation_date', 'start_publication',
33
'classes': ('collapse', 'collapse-closed')}),
34
(_('Privacy'), {'fields': ('password', 'login_required',),
35
'classes': ('collapse', 'collapse-closed')}),
36
(_('Discussion'), {'fields': ('comment_enabled',
37
'pingback_enabled')}),
38
(_('Publication'), {'fields': ('categories', 'tags',
40
list_filter = ('categories', 'authors', 'status', 'featured',
41
'login_required', 'comment_enabled', 'pingback_enabled',
42
'creation_date', 'start_publication',
43
'end_publication', 'sites')
44
list_display = ('get_title', 'get_authors', 'get_categories',
45
'get_tags', 'get_sites',
46
'comment_enabled', 'pingback_enabled',
47
'get_is_actual', 'get_is_visible', 'get_link',
48
'get_short_url', 'creation_date')
49
radio_fields = {'template': admin.VERTICAL}
50
filter_horizontal = ('categories', 'authors', 'related')
51
prepopulated_fields = {'slug': ('title', )}
52
search_fields = ('title', 'excerpt', 'content', 'tags')
53
actions = ['make_mine', 'make_published', 'make_hidden',
54
'close_comments', 'close_pingbacks',
55
'ping_directories', 'make_tweet', 'put_on_top']
57
actions_on_bottom = True
59
def __init__(self, model, admin_site):
60
self.form.admin_site = admin_site
61
super(EntryAdmin, self).__init__(model, admin_site)
64
def get_title(self, entry):
65
"""Return the title with word count and number of comments"""
66
title = _('%(title)s (%(word_count)i words)') % \
67
{'title': entry.title, 'word_count': entry.word_count}
68
comments = entry.comments.count()
70
return _('%(title)s (%(comments)i comments)') % \
71
{'title': title, 'comments': comments}
73
get_title.short_description = _('title')
75
def get_authors(self, entry):
76
"""Return the authors in HTML"""
78
authors = ['<a href="%s" target="blank">%s</a>' %
79
(reverse('zinnia_author_detail',
80
args=[author.username]),
81
author.username) for author in entry.authors.all()]
82
except NoReverseMatch:
83
authors = [author.username for author in entry.authors.all()]
84
return ', '.join(authors)
85
get_authors.allow_tags = True
86
get_authors.short_description = _('author(s)')
88
def get_categories(self, entry):
89
"""Return the categories linked in HTML"""
91
categories = ['<a href="%s" target="blank">%s</a>' %
92
(category.get_absolute_url(), category.title)
93
for category in entry.categories.all()]
94
except NoReverseMatch:
95
categories = [category.title for category in
96
entry.categories.all()]
97
return ', '.join(categories)
98
get_categories.allow_tags = True
99
get_categories.short_description = _('category(s)')
101
def get_tags(self, entry):
102
"""Return the tags linked in HTML"""
104
return ', '.join(['<a href="%s" target="blank">%s</a>' %
105
(reverse('zinnia_tag_detail',
106
args=[tag.name]), tag.name)
107
for tag in Tag.objects.get_for_object(entry)])
108
except NoReverseMatch:
110
get_tags.allow_tags = True
111
get_tags.short_description = _('tag(s)')
113
def get_sites(self, entry):
114
"""Return the sites linked in HTML"""
115
return ', '.join(['<a href="http://%(domain)s" target="blank">%(name)s</a>' %
116
site.__dict__ for site in entry.sites.all()])
117
get_sites.allow_tags = True
118
get_sites.short_description = _('site(s)')
120
def get_is_actual(self, entry):
121
"""Admin wrapper for entry.is_actual"""
122
return entry.is_actual
123
get_is_actual.boolean = True
124
get_is_actual.short_description = _('is actual')
126
def get_is_visible(self, entry):
127
"""Admin wrapper for entry.is_visible"""
128
return entry.is_visible
129
get_is_visible.boolean = True
130
get_is_visible.short_description = _('is visible')
132
def get_link(self, entry):
133
"""Return a formated link to the entry"""
134
return _('<a href="%s" target="blank">View</a>') % \
135
entry.get_absolute_url()
136
get_link.allow_tags = True
137
get_link.short_description = _('View on site')
139
def get_short_url(self, entry):
140
"""Return the short url in HTML"""
141
short_url = entry.short_url
143
return _('Unavailable')
144
return '<a href="%(url)s" target="blank">%(url)s</a>' % \
146
get_short_url.allow_tags = True
147
get_short_url.short_description = _('short url')
150
def save_model(self, request, entry, form, change):
151
"""Save the authors, update time, make an excerpt"""
152
if not form.cleaned_data.get('excerpt') and entry.status == PUBLISHED:
153
entry.excerpt = truncate_words(strip_tags(entry.content), 50)
155
if entry.pk and not request.user.has_perm('zinnia.can_change_author'):
156
form.cleaned_data['authors'] = entry.authors.all()
158
if not form.cleaned_data.get('authors'):
159
form.cleaned_data['authors'].append(request.user)
161
entry.last_update = datetime.now()
164
def queryset(self, request):
165
"""Make special filtering by user permissions"""
166
queryset = super(EntryAdmin, self).queryset(request)
167
if request.user.has_perm('zinnia.can_view_all'):
169
return request.user.entry_set.all()
171
def formfield_for_manytomany(self, db_field, request, **kwargs):
172
"""Filters the disposable authors"""
173
if db_field.name == 'authors':
174
if request.user.has_perm('zinnia.can_change_author'):
175
kwargs['queryset'] = User.objects.filter(is_staff=True)
177
kwargs['queryset'] = User.objects.filter(pk=request.user.pk)
179
return super(EntryAdmin, self).formfield_for_manytomany(
180
db_field, request, **kwargs)
182
def get_actions(self, request):
183
"""Define user actions by permissions"""
184
actions = super(EntryAdmin, self).get_actions(request)
185
if not request.user.has_perm('zinnia.can_change_author') \
186
or not request.user.has_perm('zinnia.can_view_all'):
187
del actions['make_mine']
188
if not settings.PING_DIRECTORIES:
189
del actions['ping_directories']
190
if not settings.USE_TWITTER or not settings.USE_BITLY:
191
del actions['make_tweet']
196
def make_mine(self, request, queryset):
197
"""Set the entries to the user"""
198
for entry in queryset:
199
if request.user not in entry.authors.all():
200
entry.authors.add(request.user)
201
make_mine.short_description = _('Set the entries to the user')
203
def make_published(self, request, queryset):
204
"""Set entries selected as published"""
205
queryset.update(status=PUBLISHED)
206
make_published.short_description = _('Set entries selected as published')
208
def make_hidden(self, request, queryset):
209
"""Set entries selected as hidden"""
210
queryset.update(status=HIDDEN)
211
make_hidden.short_description = _('Set entries selected as hidden')
213
def make_tweet(self, request, queryset):
214
"""Post an update on Twitter"""
216
auth = tweepy.OAuthHandler(settings.TWITTER_CONSUMER_KEY,
217
settings.TWITTER_CONSUMER_SECRET)
218
auth.set_access_token(settings.TWITTER_ACCESS_KEY,
219
settings.TWITTER_ACCESS_SECRET)
220
api = tweepy.API(auth)
221
for entry in queryset:
222
message = '%s %s' % (entry.title[:119], entry.short_url)
223
api.update_status(message)
224
make_tweet.short_description = _('Tweet entries selected')
226
def close_comments(self, request, queryset):
227
"""Close the comments for selected entries"""
228
queryset.update(comment_enabled=False)
229
close_comments.short_description = _('Close the comments for '\
232
def close_pingbacks(self, request, queryset):
233
"""Close the pingbacks for selected entries"""
234
queryset.update(pingback_enabled=False)
235
close_pingbacks.short_description = _('Close the linkbacks for selected entries')
237
def put_on_top(self, request, queryset):
238
"""Put the selected entries on top at the current date"""
239
queryset.update(creation_date=datetime.now())
240
put_on_top.short_description = _('Put the selected entries on top at the current date')
242
def ping_directories(self, request, queryset):
243
"""Ping Directories for selected entries"""
244
for directory in settings.PING_DIRECTORIES:
245
pinger = DirectoryPinger(directory, queryset)
248
for result in pinger.results:
249
if not result.get('flerror', True):
252
self.message_user(request, '%s : %s' % (directory,
255
self.message_user(request,
256
_('%(directory)s directory succesfully ' \
257
'pinged %(success)d entries.') %
258
{'directory': directory, 'success': success})
259
ping_directories.short_description = _('Ping Directories for ' \
263
entry_admin_urls = super(EntryAdmin, self).get_urls()
264
urls = patterns('django.views.generic.simple',
265
url(r'^autocomplete_tags/$', 'direct_to_template',
266
{'template': 'admin/zinnia/entry/autocomplete_tags.js',
267
'mimetype': 'application/javascript'},
268
name='zinnia_entry_autocomplete_tags'),
269
url(r'^wymeditor/$', 'direct_to_template',
270
{'template': 'admin/zinnia/entry/wymeditor.js',
271
'mimetype': 'application/javascript'},
272
name='zinnia_entry_wymeditor'),)
273
return urls + entry_admin_urls
276
MEDIA_URL = settings.MEDIA_URL
277
media = super(EntryAdmin, self).media + \
278
Media(css={'all': ('%scss/jquery.autocomplete.css' % MEDIA_URL,)},
279
js=('%sjs/jquery.js' % MEDIA_URL,
280
'%sjs/jquery.bgiframe.js' % MEDIA_URL,
281
'%sjs/jquery.autocomplete.js' % MEDIA_URL,
282
reverse('admin:zinnia_entry_autocomplete_tags'),))
284
if settings.WYSIWYG == 'wymeditor':
285
media += Media(js=('%sjs/wymeditor/jquery.wymeditor.pack.js' % MEDIA_URL,
286
reverse('admin:zinnia_entry_wymeditor')))
287
elif settings.WYSIWYG == 'tinymce':
288
from tinymce.widgets import TinyMCE
289
media += TinyMCE().media + Media(
290
js=(reverse('tinymce-js', args=('admin/zinnia/entry',)),))
292
media = property(_media)