Skip to content
Snippets Groups Projects
admin.py 13.8 KiB
Newer Older
Olivier Le Brouster's avatar
Olivier Le Brouster committed
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import copy
import itertools
Olivier Le Brouster's avatar
Olivier Le Brouster committed

from django.conf.urls import url, patterns
from django.contrib import messages, admin
from django.contrib.admin.models import LogEntry, CHANGE
from django.contrib.admin.templatetags.admin_static import static
from django.contrib.contenttypes.models import ContentType
from django.core.urlresolvers import reverse
from django.http import HttpResponseForbidden, HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.utils.translation import ugettext_lazy as _,  ungettext_lazy
from django.utils import six, formats
Olivier Le Brouster's avatar
Olivier Le Brouster committed

Dylann Cordel's avatar
Dylann Cordel committed

Olivier Le Brouster's avatar
Olivier Le Brouster committed
def get_fieldset_by_field(name, fieldsets):
    """
    Return fieldset containing a field given by name
    """
    for fieldset in fieldsets:
        options = fieldset[1]
        if name in options.get('fields', {}):
            return fieldset
    return None


def remove_field_from_fieldsets(name, fieldsets, remove_empty_fieldset=True):
    """
    Remove a field from all fieldset in fieldsets.
    """
    while True:
        fieldset = get_fieldset_by_field(name, fieldsets)
        if fieldset:
            fieldset[1]['fields'].remove(name)
            if remove_empty_fieldset and len(fieldset[1]['fields']) == 0:
                fieldsets.remove(fieldset)
        else:
            break


def add_fields_to_fieldset(
    fields,
    fieldsets,
    after_field=None,
    same_fieldset_as=None,
    default_fieldset_name=None,
Olivier Le Brouster's avatar
Olivier Le Brouster committed
    default_fieldset_position=None,
    replace_existing_field=False,
    remove_empty_fieldset=False,
):
    """
    Add fields in a fieldsets attribute

    Try to find a fieldset containing same_fieldset_as value in fields.  If not
    found a new fieldset is created with name `default_fieldset_name`, position
    `default_fieldset_position` and classes `default_fieldset_classes`.

    Fields are inserted just after after_field. If not found, they are inserted
    at the end.

    If replace_existing_field is True, existing fields in fieldsets with same
    name are removed.

    """
    if same_fieldset_as is None and after_field:
        same_fieldset_as = after_field

    # Find fieldset
    fieldset = get_fieldset_by_field(same_fieldset_as, fieldsets)

    if fieldset is None:
        fieldset = (default_fieldset_name, {
            'fields': [],
            'classes': default_fieldset_classes,
        })
        allready_inserted = False
    else:
        allready_inserted = True

    # Insert fields
    index = None
    if after_field:
        index = fieldset[1]['fields'].index(after_field) + 1

    index = index or len(fieldset[1]['fields'])
    for field in fields:
        if replace_existing_field:
            remove_field_from_fieldsets(
                field, fieldsets,
                remove_empty_fieldset=remove_empty_fieldset)
        elif get_fieldset_by_field(field, fieldsets) is not None:
            continue

        fieldset[1]['fields'].insert(index, field)
        index += 1

    # Insert fieldset if needed.
    if not allready_inserted and len(fieldset[1]['fields']) > 0:
        if default_fieldset_position is not None:
            fieldsets.insert(default_fieldset_position, fieldset)
        else:
            fieldsets.append(fieldset)


class NewsboxBaseAdmin(admin.ModelAdmin):

    list_filter = ('newsbox_publication_start_date', 'newsbox_published', )
    list_display = ['get_newsbox_title', 'newsbox_date_short', 'changestatus_link']
Olivier Le Brouster's avatar
Olivier Le Brouster committed
    list_display_links = ('get_newsbox_title',)
    fieldsets = [
        (None, {
            'fields': ['newsbox_title', 'newsbox_slug', 'newsbox_date', 'newsbox_published', ], }),
        (_('Content'), {
            'fields': ['newsbox_summary', 'newsbox_body'], }), ]
    actions = ['publish', 'unpublish']
    save_as = True

    def get_prepopulated_fields(self, request, object=None):
Dylann Cordel's avatar
Dylann Cordel committed
        pfields = super(NewsboxBaseAdmin, self).get_prepopulated_fields(request, object) or {}
        pfields.update({'newsbox_slug': ('newsbox_title',)})
        return pfields
    def changestatus_link(self, obj):
        if obj.newsbox_published:
            title = _('Unpublish this %s') % six.text_type(
                obj._meta.verbose_name)
            icon_url = static('admin/img/icon-yes.gif')
        else:
            title = _('Publish this %s') % six.text_type(
                obj._meta.verbose_name)
            icon_url = static('admin/img/icon-no.gif')
        url_name = '%s_%s_change_status' % (self.model._meta.model_name,
                                            self.model._meta.app_label)
        return '<a href="%s" title="%s"><img src="%s" alt="%s" /></a>' % (
            reverse('admin:%s' % url_name, args=[obj.pk, ]),
            title, icon_url, obj.newsbox_published)

    changestatus_link.allow_tags = True
    changestatus_link.admin_order_field = 'newsbox_published'
    changestatus_link.short_description = _('published')

    def newsbox_date_short(self, obj):
        return '<span title="%s">%s</span>' % (
            formats.date_format(obj.newsbox_publication_start_date, 'DATETIME_FORMAT'),
            formats.date_format(obj.newsbox_publication_start_date, 'SHORT_DATE_FORMAT')
        )
    newsbox_date_short.short_description = _('pub. date')
    newsbox_date_short.allow_tags = True

Olivier Le Brouster's avatar
Olivier Le Brouster committed
    def get_fieldsets(self, request, obj=None):
        """
        Prevent anoying modification of fieldsets class attribute.
        """
        fieldsets = super(NewsboxBaseAdmin, self).get_fieldsets(request, obj)
        return copy.deepcopy(fieldsets)

    def get_newsbox_title(self, obj):
        return obj.newsbox_title
    get_newsbox_title.short_description = _('Title')

    def get_urls(self):
        urls = super(NewsboxBaseAdmin, self).get_urls()
        url_name = '%s_%s_change_status' % (self.model._meta.model_name,
                                            self.model._meta.app_label)
Olivier Le Brouster's avatar
Olivier Le Brouster committed
        my_urls = patterns('', url(
            r'^([0-9]+)/change-status/$',
            self.admin_site.admin_view(self.change_status),
            name=url_name,))
Olivier Le Brouster's avatar
Olivier Le Brouster committed
        return my_urls + urls

    def change_status(self, request, news_id):
        """
        Switch the status of a news
        """

        news = get_object_or_404(self.model, pk=news_id)
        if not news.has_publish_permission(request):
            return HttpResponseForbidden(
                _('You do not have permission to publish this %s')
                % (six.text_type(self.model._meta.verbose_name),))

        news.newsbox_published = not news.newsbox_published
        news.save()
        if news.newsbox_published:
            messages.info(
                request,
                _(
                    'The %(objtype)s "%(objtitle)s" was '
                    'successfully published'
                ) % {
                    'objtype': six.text_type(self.model._meta.verbose_name),
                    'objtitle': news}
            )
        else:
            messages.info(
                request,
                _(
                    'The %(objtype)s "%(objtitle)s" was '
                    'successfully unpublished'
                ) % {
                    'objtype': six.text_type(self.model._meta.verbose_name),
                    'objtitle': news}
            )
        LogEntry.objects.log_action(
            user_id=request.user.id,
            content_type_id=ContentType.objects.get_for_model(self.model).pk,
            object_id=news_id,
            object_repr=six.text_type(news),
            action_flag=CHANGE,
        )
        return HttpResponseRedirect('../../')

    def publish(self, request, queryset):
        """
        Marks selected news items as published
        """
        ok = 0
        for news in queryset:
            if news.newsbox_published:
                continue

            if not news.has_publish_permission(request):
                messages.error(
                    request,
                    _(
                        'You do not have permission to publish the '
                        '%(objtype)s "%(objtitle)s"'
                    ) % {
                        'objtype': six.text_type(self.model._meta.verbose_name),
                        'objtitle': news
                    }
                )
                continue
            news.newsbox_published = True
            news.save()
            ok += 1

        messages.success(request, ungettext_lazy(
            '%(nb)d %(objtype)s was published',
            '%(nb)d %(objtype)s were published',
            ok) % {'nb': ok, 'objtype': self.model._meta.verbose_name})
    publish.short_description = _('Publish selected %(verbose_name_plural)s')

    def unpublish(self, request, queryset):
        """
        Marks selected news items as unpublished
        """
        ok = 0
        for news in queryset:
            if not news.newsbox_published:
                continue

            if not news.has_publish_permission(request):
                messages.error(
                    request,
                    _(
                        'You do not have permission to unpublish the '
                        '%(objtype)s "%(objtitle)s"'
                    ) % {
                        'objtype': six.text_type(self.model._meta.verbose_name),
                        'objtitle': news
                    }
                )
                continue
            news.newsbox_published = False
            news.save()
            ok += 1

        messages.success(request, ungettext_lazy(
            '%(nb)d %(objtype)s was unpublished',
            '%(nb)d %(objtype)s were unpublished',
            ok) % {
                'nb': ok,
                'objtype': self.model._meta.verbose_name,
            }
        )
    unpublish.short_description = _(
        'Unpublish selected %(verbose_name_plural)s')

    def ensure_slug_uniq_queryset(self, request, obj, slug):
Dylann Cordel's avatar
Dylann Cordel committed
        y, m, d = (obj.newsbox_publication_start_date.year,
                   obj.newsbox_publication_start_date.month,
                   obj.newsbox_publication_start_date.day)
        queryset = type(obj).objects.filter(newsbox_publication_start_date__year=y,
                                            newsbox_publication_start_date__month=m,
                                            newsbox_publication_start_date__day=d,
                                            newsbox_slug=slug)
        if obj.pk:
            queryset = queryset.exclude(pk=obj.pk)
        return queryset

    def ensure_slug_uniq(self, request, obj):
        max_length = 50

        slug = obj.newsbox_slug
        for x in itertools.count(1):
            if not self.ensure_slug_uniq_queryset(request, obj, slug).count():
                break
            suffix = "-%d" % x
            slug = "%s%s" % (obj.newsbox_slug[:max_length-len(suffix)], suffix)

        obj.newsbox_slug = slug

    def save_model(self, request, obj, form, change):
        self.ensure_slug_uniq(request, obj)
        return super(NewsboxBaseAdmin, self).save_model(request, obj, form, change)


Olivier Le Brouster's avatar
Olivier Le Brouster committed
class NewsboxAdmin(NewsboxBaseAdmin):
    pass


class NewsboxExpiredAdmin(NewsboxBaseAdmin):

    def newsbox_publication_dates_short(self, obj):
        output = []
        title = []
        css_class = []
Dylann Cordel's avatar
Dylann Cordel committed
        title.append(formats.date_format(obj.newsbox_publication_start_date,
                                         'DATETIME_FORMAT'))
        output.append('<span class="from">%s</span>' %
                      formats.date_format(obj.newsbox_publication_start_date,
                                          'SHORT_DATE_FORMAT'))
        css_class.append('from')
        if not obj.newsbox_publication_end_date:
            title.append(_('never'))
        else:
            title.append(
                formats.date_format(obj.newsbox_publication_end_date, 'DATETIME_FORMAT'))
Dylann Cordel's avatar
Dylann Cordel committed
            output.append('<span class="to">%s</span>' %
                          formats.date_format(obj.newsbox_publication_end_date,
                                              'SHORT_DATE_FORMAT'))
            css_class.append('to')
        css_class = '_'.join(css_class)
Dylann Cordel's avatar
Dylann Cordel committed
        title = _('from %(from)s to %(to)s') % {'from': title[0], 'to': title[1]}
        output = ('<span> %s </span>' % _('to')).join(output)
        return '<abbr class="newsbox_pub_dates %s" title="%s">%s</abbr>' % (
            css_class, title, output)
    newsbox_publication_dates_short.short_description = _('publication')
    newsbox_publication_dates_short.allow_tags = True
Olivier Le Brouster's avatar
Olivier Le Brouster committed
    def get_fieldsets(self, request, obj=None):
        fieldsets = super(NewsboxExpiredAdmin, self).get_fieldsets(request, obj)

        remove_field_from_fieldsets('newsbox_published', fieldsets)
        add_fields_to_fieldset(
            ['newsbox_published',
             'newsbox_publication_start_date',
             'newsbox_publication_end_date', ],
            fieldsets,
            default_fieldset_name=_('Publication'),
            default_fieldset_position=1)

        return fieldsets

    def get_list_display(self, request):
        list_display = copy.copy(super(NewsboxExpiredAdmin, self).get_list_display(request))
        try:
            index = list_display.index('newsbox_date_short')
            list_display[index] = 'newsbox_publication_dates_short'
        except ValueError:
            list_display.append('newsbox_publication_dates_short')
Olivier Le Brouster's avatar
Olivier Le Brouster committed
        return list_display

Dylann Cordel's avatar
Dylann Cordel committed

Olivier Le Brouster's avatar
Olivier Le Brouster committed
class NewsboxSEOAdmin(NewsboxBaseAdmin):

    def get_fieldsets(self, request, obj=None):
        fieldsets = super(NewsboxSEOAdmin, self).get_fieldsets(request, obj)

        add_fields_to_fieldset(
            ['newsbox_indexed',
             'newsbox_meta_description',
             'newsbox_meta_keywords', ],
            fieldsets,
            default_fieldset_name=_('SEO Settings'),
            default_fieldset_classes=('collapse',),
        )

        return fieldsets

    def get_list_display(self, request):
        list_display = super(NewsboxSEOAdmin, self).get_list_display(request)
        return list_display