import json import django from django.contrib.admin import ModelAdmin, SimpleListFilter from django.contrib import messages from django.conf.urls import url from django.core.exceptions import PermissionDenied from django.core.urlresolvers import reverse from django.http import Http404, HttpResponseRedirect, HttpResponse from django.utils.encoding import force_text from django.utils.html import escape from django.utils.translation import ugettext_lazy as _ from django import forms from django.template import loader, Context def make_published(modeladmin, request, queryset): for row in queryset.all(): row.publish() make_published.short_description = _('Publish') def make_unpublished(modeladmin, request, queryset): for row in queryset.all(): row.unpublish() make_unpublished.short_description = _('Unpublish') def http_json_response(data): return HttpResponse(json.dumps(data), content_type='application/json') class PublisherForm(forms.ModelForm): def clean(self): data = super(PublisherForm, self).clean() cleaned_data = self.cleaned_data instance = self.instance # work out which fields are unique_together unique_fields_set = instance.get_unique_together() if not unique_fields_set: return data for unique_fields in unique_fields_set: unique_filter = {} for unique_field in unique_fields: field = instance.get_field(unique_field) # Get value from the form or the model if field.editable: unique_filter[unique_field] = cleaned_data[unique_field] else: unique_filter[unique_field] = getattr(instance, unique_field) # try to find if any models already exist in the db; # I find all models and then exclude those matching the current model. existing_instances = type(instance).objects \ .filter(**unique_filter) \ .exclude(pk=instance.pk) if instance.publisher_linked: existing_instances = existing_instances.exclude(pk=instance.publisher_linked.pk) if existing_instances: for unique_field in unique_fields: self._errors[unique_field] = self.error_class( [_('This value must be unique.')]) return data class PublisherAdmin(ModelAdmin): form = PublisherForm change_form_template = 'publisher/change_form.html' # publish or unpublish actions sometime makes the plugins disappear from page # so we disable it for now, until we can investigate it further. # actions = (make_published, make_unpublished, ) list_display = ('publisher_object_title', 'publisher_publish', 'publisher_status', ) url_name_prefix = None class Media: js = ( 'publisher/publisher.js', ) css = { 'all': ('publisher/publisher.css', ), } def __init__(self, model, admin_site): super(PublisherAdmin, self).__init__(model, admin_site) self.request = None self.url_name_prefix = '%(app_label)s_%(module_name)s_' % { 'app_label': self.model._meta.app_label, 'module_name': self.model._meta.model_name, } # Reverse URL strings used in multiple places.. self.publish_reverse = '%s:%spublish' % ( self.admin_site.name, self.url_name_prefix, ) self.unpublish_reverse = '%s:%sunpublish' % ( self.admin_site.name, self.url_name_prefix, ) self.revert_reverse = '%s:%srevert' % ( self.admin_site.name, self.url_name_prefix, ) self.changelist_reverse = '%s:%schangelist' % ( self.admin_site.name, self.url_name_prefix, ) def has_publish_permission(self, request, obj=None): opts = self.opts return request.user.has_perm('%s.can_publish' % opts.app_label) def publisher_object_title(self, obj): return u'%s' % obj publisher_object_title.short_description = 'Title' def publisher_status(self, obj): if not self.has_publish_permission(self.request, obj): return '' template_name = 'publisher/change_list_publish_status.html' publish_btn = None if obj.is_dirty: publish_btn = reverse(self.publish_reverse, args=(obj.pk, )) t = loader.get_template(template_name) c = Context({ 'publish_btn': publish_btn, }) if django.VERSION >= (1, 10): return t.render(c.flatten()) else: return t.render(c) publisher_status.short_description = 'Last Changes' publisher_status.allow_tags = True def publisher_publish(self, obj): template_name = 'publisher/change_list_publish.html' is_published = False if obj.publisher_linked and obj.is_draft: is_published = True t = loader.get_template(template_name) c = Context({ 'object': obj, 'is_published': is_published, 'has_publish_permission': self.has_publish_permission(self.request, obj), 'publish_url': reverse(self.publish_reverse, args=(obj.pk, )), 'unpublish_url': reverse(self.unpublish_reverse, args=(obj.pk, )), }) if django.VERSION >= (1, 10): return t.render(c.flatten()) else: return t.render(c) publisher_publish.short_description = 'Published' publisher_publish.allow_tags = True def get_queryset(self, request): # hack! We need request.user to check user publish perms self.request = request qs = self.model.publisher_manager.drafts() ordering = self.get_ordering(request) if ordering: qs = qs.order_by(*ordering) return qs queryset = get_queryset def get_urls(self): urls = super(PublisherAdmin, self).get_urls() publish_name = '%spublish' % (self.url_name_prefix, ) unpublish_name = '%sunpublish' % (self.url_name_prefix, ) revert_name = '%srevert' % (self.url_name_prefix, ) publish_urls = [ url(r'^(?P<object_id>\d+)/publish/$', self.publish_view, name=publish_name), url(r'^(?P<object_id>\d+)/unpublish/$', self.unpublish_view, name=unpublish_name), url(r'^(?P<object_id>\d+)/revert/$', self.revert_view, name=revert_name), ] return publish_urls + urls def get_model_object(self, request, object_id): obj = self.model.objects.get(pk=object_id) if not self.has_change_permission(request, obj): raise PermissionDenied if obj is None: raise Http404(_('%s object with primary key %s does not exist.') % ( force_text(self.model._meta.verbose_name), escape(object_id) )) if not self.has_change_permission(request) and not self.has_add_permission(request): raise PermissionDenied return obj def revert_view(self, request, object_id): obj = self.get_model_object(request, object_id) if not self.has_publish_permission(request, obj): raise PermissionDenied obj.revert_to_public() if not request.is_ajax(): messages.success(request, _('Draft has been revert to the public version.')) return HttpResponseRedirect(reverse(self.changelist_reverse)) return http_json_response({'success': True}) def unpublish_view(self, request, object_id): obj = self.get_model_object(request, object_id) if not self.has_publish_permission(request, obj): raise PermissionDenied obj.unpublish() if not request.is_ajax(): messages.success(request, _('Published version has been deleted.')) return HttpResponseRedirect(reverse(self.changelist_reverse)) return http_json_response({'success': True}) def publish_view(self, request, object_id): obj = self.get_model_object(request, object_id) if not self.has_publish_permission(request, obj): raise PermissionDenied obj.publish() if not request.is_ajax(): messages.success(request, _('Draft version has been published.')) return HttpResponseRedirect(reverse(self.changelist_reverse)) return http_json_response({'success': True}) def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None): obj = context.get('original', None) if not obj: return super(PublisherAdmin, self).render_change_form( request, context, add, change, form_url, obj=None) if not self.has_publish_permission(request, obj): context['has_publish_permission'] = False else: context['has_publish_permission'] = True publish_btn = None if obj.is_dirty: publish_btn = reverse(self.publish_reverse, args=(obj.pk, )) preview_draft_btn = None if callable(getattr(obj, 'get_absolute_url', None)): preview_draft_btn = True unpublish_btn = None if obj.is_draft and obj.publisher_linked: unpublish_btn = reverse(self.unpublish_reverse, args=(obj.pk, )) revert_btn = None if obj.is_dirty and obj.publisher_linked: revert_btn = reverse(self.revert_reverse, args=(obj.pk, )) context.update({ 'publish_btn_live': publish_btn, 'preview_draft_btn': preview_draft_btn, 'unpublish_btn': unpublish_btn, 'revert_btn': revert_btn, }) return super(PublisherAdmin, self).render_change_form( request, context, add, change, form_url, obj=None) try: from hvad.admin import TranslatableAdmin from hvad.manager import FALLBACK_LANGUAGES except ImportError: pass else: class PublisherHvadAdmin(TranslatableAdmin, PublisherAdmin): change_form_template = 'publisher/hvad/change_form.html' def queryset(self, request): # hack! We need request.user to check user publish perms self.request = request language = self._language(request) languages = [language] for lang in FALLBACK_LANGUAGES: if lang not in languages: languages.append(lang) qs = self.model._default_manager.untranslated().use_fallbacks(*languages) qs = qs.filter(publisher_is_draft=True) ordering = getattr(self, 'ordering', None) or () if ordering: qs = qs.order_by(*ordering) return qs try: from parler.admin import TranslatableAdmin as PTranslatableAdmin except ImportError: pass else: class PublisherParlerAdmin(PTranslatableAdmin, PublisherAdmin): change_form_template = 'publisher/parler/change_form.html' def queryset(self, request): # hack! We need request.user to check user publish perms self.request = request qs = self.model.objects qs_language = self.get_queryset_language(request) if qs_language: qs = qs.language(qs_language) qs = qs.filter(publisher_is_draft=True) ordering = getattr(self, 'ordering', None) or () if ordering: qs = qs.order_by(*ordering) return qs class PublisherPublishedFilter(SimpleListFilter): title = _('Published') parameter_name = 'published' def lookups(self, request, model_admin): return ( ('1', _('Yes')), ('0', _('No')) ) def queryset(self, request, queryset): try: value = int(self.value()) except TypeError: return queryset isnull = not value return queryset.filter(publisher_linked__isnull=isnull)