zinnia.admin.entry
Covered: 154 lines
Missed: 100 lines
Skipped 39 lines
Percent: 60 %
  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"""
 25
    form = EntryAdminForm
 26
    date_hierarchy = 'creation_date'
 27
    fieldsets = ((_('Content'), {'fields': ('title', 'content',
 28
                                            'image', 'status')}),
 29
                 (_('Options'), {'fields': ('featured', 'excerpt', 'template',
 30
                                            'related', 'authors',
 31
                                            'creation_date', 'start_publication',
 32
                                            'end_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',
 39
                                                'sites', 'slug')}))
 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']
 56
    actions_on_top = True
 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()
 69
        if comments:
 70
            return _('%(title)s (%(comments)i comments)') % \
 71
                   {'title': title, 'comments': comments}
 72
        return title
 73
    get_title.short_description = _('title')
 75
    def get_authors(self, entry):
 76
        """Return the authors in HTML"""
 77
        try:
 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"""
 90
        try:
 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"""
103
        try:
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:
109
            return entry.tags
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
142
        if not short_url:
143
            return _('Unavailable')
144
        return '<a href="%(url)s" target="blank">%(url)s</a>' % \
145
               {'url': short_url}
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()
162
        entry.save()
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'):
168
            return queryset
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)
176
            else:
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']
193
        return actions
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"""
215
        import tweepy
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 '\
230
                                         'selected entries')
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)
246
            pinger.join()
247
            success = 0
248
            for result in pinger.results:
249
                if not result.get('flerror', True):
250
                    success += 1
251
                else:
252
                    self.message_user(request, '%s : %s' % (directory,
253
                                                            result['message']))
254
            if success:
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 ' \
260
                                           'selected entries')
262
    def get_urls(self):
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
275
    def _media(self):
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',)),))
291
        return media
292
    media = property(_media)