# -*- coding: utf-8 -*- from __future__ import unicode_literals import copy import itertools from django.conf.urls import url 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 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, default_fieldset_classes=[], 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_status', ) list_display_links = ('get_newsbox_title',) fieldsets = [ (None, { 'fields': ['newsbox_title', 'newsbox_slug', 'newsbox_date', 'newsbox_status', ], }), (_('Content'), { 'fields': ['newsbox_summary', 'newsbox_body'], }), ] actions = ['publish', 'unpublish'] save_as = True def get_list_display(self, request): if not getattr(self.__class__, 'list_display', None): self.list_display = ['get_newsbox_title', 'newsbox_date_short', 'changestatus_link'] return super(NewsboxBaseAdmin, self).get_list_display(request) def get_prepopulated_fields(self, request, object=None): 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_status == obj.STATUS_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.get_newsbox_status_display()) changestatus_link.allow_tags = True changestatus_link.admin_order_field = 'newsbox_status' 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 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): url_prefix = '%s_%s_' % (self.model._meta.model_name, self.model._meta.app_label) return [ url(r'^([0-9]+)/change-status/$', self.admin_site.admin_view(self.change_status), name='%schange_status' % url_prefix,), url(r'^([0-9]+)/publish/$', self.admin_site.admin_view(self.publish_one), name='%spublish_one' % url_prefix,), url(r'^([0-9]+)/unpublish/$', self.admin_site.admin_view(self.unpublish_one), name='%sunpublish_one' % url_prefix,) ] + super(NewsboxBaseAdmin, self).get_urls() def change_status(self, request, news_id): """ Switch the status of a news """ obj = get_object_or_404(self.model, pk=news_id) if not obj.has_publish_permission(request.user): return HttpResponseForbidden( _('You do not have permission to publish this %s') % (six.text_type(self.model._meta.verbose_name),)) old_status = obj.newsbox_status if obj.newsbox_status == obj.STATUS_PUBLISHED: obj.newsbox_status = obj.STATUS_ARCHIVED else: obj.newsbox_status = obj.STATUS_PUBLISHED obj.save() log_msg = _('publication status changed from %(old)s to %(new)s') % { 'old': old_status, 'new_status': obj.newsbox_status, } if obj.newsbox_status == obj.STATUS_PUBLISHED: msg = _('The %(objtype)s "%(objtitle)s" has been successfully published') else: msg = _('The %(objtype)s "%(objtitle)s" has been successfully unpublished') messages.info(request, msg % {'objtype': six.text_type(self.model._meta.verbose_name), 'objtitle': six.text_type(obj)}) 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(obj), action_flag=CHANGE, change_message=log_msg, ) return HttpResponseRedirect(request.GET.get('next', '../../')) def publish_one(self, request, news_id): obj = get_object_or_404(self.model, pk=news_id) if not obj.has_publish_permission(request.user): return HttpResponseForbidden( _('You do not have permission to publish this %s') % (six.text_type(self.model._meta.verbose_name),)) self.publish(request, self.model.objects.filter(pk=news_id)) return HttpResponseRedirect(request.GET.get('next', '../../')) def unpublish_one(self, request, news_id): obj = get_object_or_404(self.model, pk=news_id) if not obj.has_publish_permission(request.user): return HttpResponseForbidden( _('You do not have permission to publish this %s') % (six.text_type(self.model._meta.verbose_name),)) self.unpublish(request, self.model.objects.filter(pk=news_id)) return HttpResponseRedirect(request.GET.get('next', '../../')) def publish(self, request, queryset): """ Marks selected news items as published """ ok = 0 content_type_id = ContentType.objects.get_for_model(self.model).pk for obj in queryset: if obj.is_published: continue if not obj.has_publish_permission(request.user): 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': six.text_type(obj) } ) continue obj.publish() LogEntry.objects.log_action(user_id=request.user.id, content_type_id=content_type_id, object_id=obj.pk, object_repr=six.text_type(obj), action_flag=CHANGE, change_message=_('Published'),) ok += 1 last_obj_ok = obj if ok == 1: messages.success(request, _('The %(objtype)s "%(objtitle)s" has been published') % { 'objtype': self.model._meta.verbose_name, 'objtitle': last_obj_ok, }) else: messages.success(request, ungettext_lazy( '%(nb)d %(objtype)s has been published', '%(nb)d %(objtype)s have been published', ok) % {'nb': ok, 'objtype': self.model._meta.verbose_name_plural,}) publish.short_description = _('Publish selected %(verbose_name_plural)s') def unpublish(self, request, queryset): """ Marks selected news items as unpublished """ ok = 0 content_type_id = ContentType.objects.get_for_model(self.model).pk for obj in queryset: if not obj.is_published: continue if not obj.has_publish_permission(request.user): 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': six.text_type(obj) } ) continue obj.unpublish() LogEntry.objects.log_action(user_id=request.user.id, content_type_id=content_type_id, object_id=obj.pk, object_repr=six.text_type(obj), action_flag=CHANGE, change_message=_('Unpublished'),) ok += 1 last_obj_ok = obj if ok == 1: messages.success(request, _('The %(objtype)s "%(objtitle)s" has been unpublished') % { 'objtype': self.model._meta.verbose_name, 'objtitle': last_obj_ok, }) else: messages.success(request, ungettext_lazy( '%(nb)d %(objtype)s has been unpublished', '%(nb)d %(objtype)s have been unpublished', ok) % {'nb': ok, 'objtype': self.model._meta.verbose_name_plural}) unpublish.short_description = _('Archive selected %(verbose_name_plural)s') def ensure_slug_uniq_queryset(self, request, obj, slug): 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) class NewsboxAdmin(NewsboxBaseAdmin): pass class NewsboxExpiredAdmin(NewsboxBaseAdmin): def newsbox_publication_dates_short(self, obj): output = [] title = [] css_class = [] 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')) 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) 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 def get_fieldsets(self, request, obj=None): fieldsets = super(NewsboxExpiredAdmin, self).get_fieldsets(request, obj) for fieldset in fieldsets: if 'newsbox_publication_end_date' in fieldset[1]['fields']: return fieldsets remove_field_from_fieldsets('newsbox_status', fieldsets) add_fields_to_fieldset( ['newsbox_status', 'newsbox_publication_start_date', 'newsbox_publication_end_date', ], fieldsets, default_fieldset_name=_('Publication settings'), default_fieldset_position=1) return fieldsets def get_list_display(self, request): list_display = list(super(NewsboxExpiredAdmin, self).get_list_display(request)) if 'newsbox_publication_dates_short' not in list_display: try: index = list_display.index('newsbox_date_short') list_display[index] = 'newsbox_publication_dates_short' except ValueError: list_display.append('newsbox_publication_dates_short') return list_display 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