class FoiAttachmentAdmin(admin.ModelAdmin): raw_id_fields = ('belongs_to', 'redacted', 'converted') ordering = ('-id',) list_display = ('name', 'filetype', 'admin_link_message', 'approved', 'can_approve',) list_filter = ('can_approve', 'approved', 'is_redacted', 'is_converted', make_nullfilter('redacted', _(u'Has redacted version')), make_nullfilter('converted', _(u'Has converted version')) ) search_fields = ['name'] actions = ['approve', 'cannot_approve', 'convert'] def approve(self, request, queryset): rows_updated = queryset.update(approved=True) self.message_user(request, _("%d attachment(s) successfully approved." % rows_updated)) approve.short_description = _("Mark selected as approved") def cannot_approve(self, request, queryset): rows_updated = queryset.update(can_approve=False) self.message_user(request, _("%d attachment(s) successfully marked as not approvable." % rows_updated)) cannot_approve.short_description = _("Mark selected as NOT approvable") def convert(self, request, queryset): if not queryset: return for instance in queryset: if (instance.filetype in FoiAttachment.CONVERTABLE_FILETYPES or instance.name.endswith(FoiAttachment.CONVERTABLE_FILETYPES)): convert_attachment_task.delay(instance.pk) self.message_user(request, _("Conversion tasks started.")) convert.short_description = _("Convert to PDF")
class FoiAttachmentAdmin(admin.ModelAdmin): raw_id_fields = ('belongs_to', 'redacted', 'converted') ordering = ('-id', ) list_display = ( 'name', 'filetype', 'admin_link_message', 'approved', 'can_approve', ) list_filter = ('can_approve', 'approved', 'is_redacted', 'is_converted', make_nullfilter('redacted', _('Has redacted version')), make_nullfilter('converted', _('Has converted version'))) search_fields = ['name'] actions = ['approve', 'cannot_approve', 'convert'] def admin_link_message(self, obj): return format_html( '<a href="{}">{}</a>', reverse('admin:foirequest_foimessage_change', args=(obj.belongs_to_id, )), _('See FoiMessage')) admin_link_message.allow_tags = True def approve(self, request, queryset): rows_updated = queryset.update(approved=True) self.message_user( request, _("%d attachment(s) successfully approved." % rows_updated)) approve.short_description = _("Mark selected as approved") def cannot_approve(self, request, queryset): rows_updated = queryset.update(can_approve=False) self.message_user( request, _("%d attachment(s) successfully marked as not approvable." % rows_updated)) cannot_approve.short_description = _("Mark selected as NOT approvable") def convert(self, request, queryset): if not queryset: return count = 0 for instance in queryset: ft = instance.filetype.lower() name = instance.name.lower() if can_convert_to_pdf(ft, name=name): count += 1 convert_attachment_task.delay(instance.pk) self.message_user(request, _("Conversion tasks started: %s") % count) convert.short_description = _("Convert to PDF")
class ProblemReportAdmin(admin.ModelAdmin): date_hierarchy = 'timestamp' raw_id_fields = ('message', 'user', 'moderator') list_filter = ( 'auto_submitted', 'resolved', 'kind', make_nullfilter('claimed', _('Claimed')), make_nullfilter('escalated', _('Escalated')), ) list_display = ( 'kind', 'timestamp', 'admin_link_message', 'auto_submitted', 'moderator', 'resolved', ) actions = ['resolve_all'] def get_queryset(self, request): qs = super().get_queryset(request) qs = qs.select_related('message', 'moderator') return qs def admin_link_message(self, obj): return format_html( '<a href="{}">{}</a>', reverse('admin:foirequest_foimessage_change', args=(obj.message_id, )), str(obj.message.subject)) def save_model(self, request, obj, form, change): super().save_model(request, obj, form, change) if 'resolved' in form.changed_data and obj.resolved: sent = obj.resolve(request.user, resolution=obj.resolution) if sent: self.message_user(request, _('User will be notified of resolution')) def resolve_all(self, request, queryset): count = 0 for problem in queryset.filter(resolved=False): sent = problem.resolve(request.user, resolution='') if sent: count += 1 self.message_user( request, _('Problems marked as resolved, {count} users will be notified.'). format(count=count)) resolve_all.short_description = _('Resolve selected')
class FoiMessageAdmin(admin.ModelAdmin): save_on_top = True list_display = ( 'subject', 'timestamp', 'sender_email', 'recipient_email', ) list_filter = ('is_postal', 'is_response', 'sent', 'status', 'deliverystatus__status', make_nullfilter('deliverystatus', _('Has delivery status'))) search_fields = ['subject', 'sender_email', 'recipient_email'] ordering = ('-timestamp', ) date_hierarchy = 'timestamp' exclude = ('original', ) raw_id_fields = ('request', 'sender_user', 'sender_public_body', 'recipient_public_body') inlines = [ DeliveryStatusInline, FoiAttachmentInline, ] actions = ['check_delivery_status'] def check_delivery_status(self, request, queryset): from .tasks import check_delivery_status for message in queryset: check_delivery_status.delay(message.id, extended=True) self.message_user( request, _("Selected messages are being checked for delivery.")) check_delivery_status.short_description = _("Check delivery status")
class VenueRequestItemAdmin(admin.ModelAdmin): list_display = ( 'venue', 'timestamp', ) list_filter = ( 'foirequest__status', 'foirequest__resolution', make_nullfilter('checked_date', 'Wurde geprüft'), ) date_hierarchy = 'timestamp' actions = ['remove_unconfirmed'] def get_querset(self, request): return super().get_querset().prefetch_related('venue', 'foirequest') def remove_unconfirmed(self, request, queryset): week_ago = timezone.now() - timedelta(days=7) queryset = queryset.filter( timestamp__lt=week_ago, foirequest__status='awaiting_user_confirmation') for vri in queryset: venue = vri.venue vri.delete() venue.update_from_items() remove_unconfirmed.short_description = 'Alte unbestaetigte entfernen'
class VenueRequestItemAdmin(admin.ModelAdmin): list_display = ( "venue", "timestamp", ) list_filter = ( "foirequest__status", "foirequest__resolution", make_nullfilter("checked_date", "Wurde geprüft"), ) date_hierarchy = "timestamp" raw_id_fields = ("venue", "foirequest", "publicbody") actions = ["remove_unconfirmed"] def get_querset(self, request): return super().get_querset().prefetch_related("venue", "foirequest") def remove_unconfirmed(self, request, queryset): week_ago = timezone.now() - timedelta(days=7) queryset = queryset.filter( timestamp__lt=week_ago, foirequest__status="awaiting_user_confirmation") for vri in queryset: venue = vri.venue vri.delete() venue.update_from_items() remove_unconfirmed.short_description = "Alte unbestaetigte entfernen"
class JurisdictionAdmin(admin.ModelAdmin): prepopulated_fields = {"slug": ("name", )} list_filter = [ 'hidden', 'rank', make_nullfilter('region', _('Has region')), ] list_display = ['name', 'hidden', 'rank'] raw_id_fields = ('region', )
class GuidanceAdmin(admin.ModelAdmin): raw_id_fields = ( 'message', 'user', ) date_hierarchy = 'timestamp' list_display = ('message', 'action', 'timestamp') list_filter = ('action', ('message', ForeignKeyFilter), make_nullfilter('rule', _('Has rule'))) def get_queryset(self, request): qs = super().get_queryset(request) qs = qs.prefetch_related('message', 'action') return qs
class FoodSafetyReportAdmin(admin.ModelAdmin): change_list_template = ( 'admin/froide_food/foodsafetyreport/change_list.html') raw_id_fields = ( 'venue', 'request_item', 'message', 'attachment', 'amenity', ) list_display = ( 'venue', 'date', 'complaints', ) date_hierarchy = 'date' list_filter = ( 'complaints', make_nullfilter('attachment', 'Hat Anhang'), ) def changelist_view(self, request, extra_context=None): response = super().changelist_view( request, extra_context=extra_context, ) try: qs = response.context_data['cl'].queryset except (AttributeError, KeyError): return response response.context_data['distinct_venues'] = ( qs.distinct('venue').order_by('venue').count()) return response
class FoodSafetyReportAdmin(admin.ModelAdmin): change_list_template = "admin/froide_food/foodsafetyreport/change_list.html" raw_id_fields = ( "venue", "request_item", "message", "attachment", "amenity", ) list_display = ( "venue", "date", "complaints", ) date_hierarchy = "date" list_filter = ( "complaints", "disgusting", make_nullfilter("attachment", "Hat Anhang"), ) def changelist_view(self, request, extra_context=None): response = super().changelist_view( request, extra_context=extra_context, ) try: qs = response.context_data["cl"].queryset except (AttributeError, KeyError): return response response.context_data["distinct_venues"] = ( qs.distinct("venue").order_by("venue").count()) return response
class DeferredMessageAdmin(admin.ModelAdmin): model = DeferredMessage list_filter = ('delivered', make_nullfilter('request', _('Has request')), 'spam') search_fields = ['recipient'] date_hierarchy = 'timestamp' ordering = ('-timestamp', ) list_display = ( 'recipient', 'timestamp', 'spam', 'delivered', 'get_email_details', 'request', ) raw_id_fields = ('request', ) actions = ['deliver_no_spam', 'redeliver', 'redeliver_subject'] # Reduce per page because parsing emails is heavy list_per_page = 20 save_on_top = True def get_email_details(self, obj): parser = EmailParser() email = parser.parse(BytesIO(obj.encoded_mail())) return '%s (%s...)' % (email['from'][1], email.get('subject')[:20]) get_email_details.short_description = _('email details') def redeliver_subject(self, request, queryset): parser = EmailParser() for deferred in queryset: email = parser.parse(BytesIO(deferred.encoded_mail())) match = SUBJECT_REQUEST_ID.search(email['subject']) if match is not None: try: req = FoiRequest.objects.get(pk=match.group(1)) deferred.redeliver(req) except FoiRequest.DoesNotExist: continue redeliver_subject.short_description = _("Auto-Redeliver based on subject") def deliver_no_spam(self, request, queryset): for deferred in queryset: if deferred.request is not None: deferred.spam = False if deferred.delivered: deferred.save() else: deferred.redeliver(deferred.request) deliver_no_spam.short_description = _("Deliver and mark as no spam") def redeliver(self, request, queryset, auto=False): """ Redeliver undelivered mails """ opts = self.model._meta # Check that the user has change permission for the actual model if not self.has_change_permission(request): raise PermissionDenied Form = get_fk_form_class(self.model, 'request', self.admin_site) # User has already chosen the other req if request.POST.get('obj'): f = Form(request.POST) if f.is_valid(): req = f.cleaned_data['obj'] for deferred in queryset: deferred.redeliver(req) self.message_user(request, _("Successfully triggered redelivery.")) return None else: f = Form() context = { 'opts': opts, 'queryset': queryset, 'media': self.media, 'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME, 'form': f, 'applabel': opts.app_label } # Display the confirmation page return TemplateResponse(request, 'foirequest/admin_redeliver.html', context) redeliver.short_description = _("Redeliver to...")
class VenueRequestAdmin(LeafletGeoAdmin): display_raw = True inlines = [VenueRequestItemInlineAdmin] date_hierarchy = 'last_request' list_filter = ('last_status', 'last_resolution', make_nullfilter('last_request', 'Hat Anfrage'), make_nullfilter('geo', 'Hat eigene Koordinate')) list_display = ('name', 'last_request', 'get_last_status_display', 'get_last_resolution_display', 'get_provider') search_fields = ('name', 'ident') actions = ['merge_venues'] def get_urls(self): urls = super(VenueRequestAdmin, self).get_urls() my_urls = [ url(r'^export-users/$', self.admin_site.admin_view(self.export_users), name='froide_food-admin-export_users'), ] return my_urls + urls def export_users(self, request): if not request.method == 'GET': raise PermissionDenied if not request.user.is_superuser: raise PermissionDenied fr_ids = set( VenueRequestItem.objects.filter( timestamp__year__gte=2019).values_list('foirequest_id', flat=True)) # -first_message, so last value overwrites user_id first_req_timestamp = dict( FoiRequest.objects.filter( id__in=fr_ids).order_by('-first_message').values_list( 'user_id', 'first_message')) queryset = User.objects.filter( id__in=list(first_req_timestamp.keys()), is_active=True, date_joined__year__gte=2019, ) def generator(queryset): for u in queryset: f = u.foirequest_set.all().order_by('first_message')[0] if f.id not in fr_ids: continue diff = abs((first_req_timestamp[u.id] - u.date_joined).seconds) if diff < 2: yield u fields = ('id', "first_name", "last_name", "email", 'date_joined') stream = User.export_csv(generator(queryset), fields=fields) return export_csv_response(stream) def get_last_status_display(self, obj): return FoiRequest.STATUS_RESOLUTION_DICT.get(obj.last_status, (obj.last_status, ))[0] get_last_status_display.short_description = 'letzter Status' def get_provider(self, obj): return obj.ident.split(':')[0] get_provider.short_description = 'provider' def get_last_resolution_display(self, obj): return FoiRequest.STATUS_RESOLUTION_DICT.get( obj.last_resolution, (obj.last_resolution, ))[0] get_last_resolution_display.short_description = 'letztes Ergebnis' def merge_venues(self, request, queryset): """ Set articles selected as published. """ from .utils import merge_venues merge_venues(queryset) merge_venues.short_description = 'Merge venues'
class FoiRequestAdmin(admin.ModelAdmin, AdminTagAllMixIn): form = FoiRequestAdminForm prepopulated_fields = {"slug": ("title", )} inlines = [ FoiMessageInline, ] list_display = ('title', 'first_message', 'secret_address', 'request_page', 'public_body', 'status', 'visibility') list_filter = ('jurisdiction', 'first_message', 'last_message', 'status', 'resolution', 'is_foi', 'checked', 'public', 'visibility', 'is_blocked', 'not_publishable', 'campaign', make_nullfilter('same_as', _('Has same request')), ('user', ForeignKeyFilter), ('public_body', ForeignKeyFilter), ('project', ForeignKeyFilter), FoiRequestTagsFilter, make_greaterzerofilter('costs', _('Costs given'))) search_fields = ['title', 'description', 'secret_address', 'reference'] ordering = ('-last_message', ) date_hierarchy = 'first_message' tag_all_config = ('tags', reverse_lazy('api:request-tags-autocomplete')) actions = [ 'mark_checked', 'mark_not_foi', 'mark_successfully_resolved', 'mark_refused', 'tag_all', 'mark_same_as', 'update_index', 'confirm_request', 'set_visible_to_user', 'unpublish', 'add_to_project', 'unblock_request', 'close_requests' ] raw_id_fields = ('same_as', 'public_body', 'user', 'project', 'jurisdiction', 'law') save_on_top = True def request_page(self, obj): return format_html('<a href="{}">{}</a>', obj.get_absolute_url(), _('request page')) def mark_checked(self, request, queryset): rows_updated = queryset.update(checked=True) update_foirequest_index(queryset) self.message_user( request, _("%d request(s) successfully marked as checked." % rows_updated)) mark_checked.short_description = _("Mark selected requests as checked") def mark_not_foi(self, request, queryset): rows_updated = queryset.update( is_foi=False, public=False, visibility=FoiRequest.VISIBLE_TO_REQUESTER) update_foirequest_index(queryset) self.message_user( request, _("%d request(s) successfully marked as not FoI." % rows_updated)) mark_not_foi.short_description = _("Mark selected requests as not FoI") def mark_successfully_resolved(self, request, queryset): rows_updated = queryset.update(status='resolved', resolution='successful') update_foirequest_index(queryset) self.message_user( request, _("%d request(s) have been marked as successfully resolved." % rows_updated)) mark_successfully_resolved.short_description = _( "Mark successfully resolved") def mark_refused(self, request, queryset): rows_updated = queryset.update(status='resolved', resolution='refused') update_foirequest_index(queryset) self.message_user( request, _("%d request(s) have been marked as refused." % rows_updated)) mark_refused.short_description = _("Mark as refused") def update_index(self, request, queryset): update_foirequest_index(queryset) self.message_user( request, _("%d request(s) will be updated in the search index." % queryset.count())) update_index.short_description = _("Update search index") def mark_same_as(self, request, queryset): """ Mark selected requests as same as the one we are choosing now. """ opts = self.model._meta # Check that the user has change permission for the actual model if not self.has_change_permission(request): raise PermissionDenied Form = get_fk_form_class(self.model, 'same_as', self.admin_site) # User has already chosen the other req if request.POST.get('obj'): f = Form(request.POST) if f.is_valid(): req = f.cleaned_data['obj'] queryset.update(same_as=req) count = FoiRequest.objects.filter(same_as=req).count() FoiRequest.objects.filter(id=req.id).update( same_as_count=count) update_foirequest_index(queryset) self.message_user( request, _("Successfully marked requests as identical.")) # Return None to display the change list page again. return None else: f = Form() context = { 'opts': opts, 'queryset': queryset, 'media': self.media, 'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME, 'form': f, 'applabel': opts.app_label } # Display the confirmation page return TemplateResponse(request, 'foirequest/admin/mark_same_as.html', context) mark_same_as.short_description = _( "Mark selected requests as identical to...") def confirm_request(self, request, queryset): foirequest = queryset[0] if foirequest.status != 'awaiting_user_confirmation': self.message_user(request, _("Request not in correct state!")) return None self.message_user(request, _("Message send successfully!")) req_service = ActivatePendingRequestService({'foirequest': foirequest}) foirequest = req_service.process(request=None) return None confirm_request.short_description = _("Confirm request if unconfirmed") def set_visible_to_user(self, request, queryset): queryset.update(visibility=FoiRequest.VISIBLE_TO_REQUESTER) update_foirequest_index(queryset) self.message_user( request, _("Selected requests are now only visible to requester.")) set_visible_to_user.short_description = _("Set only visible to requester") def unpublish(self, request, queryset): queryset.update(public=False) update_foirequest_index(queryset) self.message_user(request, _("Selected requests are now unpublished.")) unpublish.short_description = _("Unpublish") def unblock_request(self, request, queryset): for req in queryset: mes = req.messages[0] mes.timestamp = timezone.now() if req.law: req.due_date = req.law.calculate_due_date() req.is_blocked = False req.first_message = mes.timestamp req.save() mes.save() mes.force_resend() unblock_request.short_description = _( "Unblock requests and send first message") def close_requests(self, request, queryset): queryset.update(closed=True) update_foirequest_index(queryset) close_requests.short_description = _("Close requests") def add_to_project(self, request, queryset): """ Mark selected requests as same as the one we are choosing now. """ opts = self.model._meta # Check that the user has change permission for the actual model if not self.has_change_permission(request): raise PermissionDenied queryset = queryset.filter(project__isnull=True) Form = get_fk_form_class(self.model, 'project', self.admin_site) # User has already chosen the other req if request.POST.get('obj'): f = Form(request.POST) if f.is_valid(): project = f.cleaned_data['obj'] project.add_requests(queryset) self.message_user(request, _("Successfully added requests to project.")) # Return None to display the change list page again. return None else: f = Form() context = { 'opts': opts, 'queryset': queryset, 'media': self.media, 'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME, 'form': f, 'applabel': opts.app_label } # Display the confirmation page return TemplateResponse(request, 'foirequest/admin/add_to_project.html', context) add_to_project.short_description = _("Add selected requests to project...")
class DeferredMessageAdmin(admin.ModelAdmin): model = DeferredMessage list_filter = ('delivered', make_nullfilter('request', _('Has request')), 'spam') search_fields = ( 'recipient', 'sender', ) date_hierarchy = 'timestamp' ordering = ('-timestamp', ) list_display = ( 'recipient', 'timestamp', 'spam', 'delivered', 'sender', 'request_last_message', 'request_status', 'request_page', ) raw_id_fields = ('request', ) actions = [ 'mark_as_spam', 'deliver_no_spam', 'redeliver', 'redeliver_subject', 'close_request' ] save_on_top = True def get_queryset(self, request): qs = super().get_queryset(request) qs = qs.select_related('request') return qs def request_last_message(self, obj): if obj.request: return obj.request.last_message def request_status(self, obj): if obj.request: return obj.request.get_status_display() def request_page(self, obj): if obj.request: return format_html('<a href="{}">{}</a>', obj.request.get_absolute_url(), obj.request.title) def close_request(self, request, queryset): for mes in queryset: mes.request.closed = True mes.request.save() return None close_request.short_description = _('Close associated requests') def redeliver_subject(self, request, queryset): parser = EmailParser() for deferred in queryset: email = parser.parse(BytesIO(deferred.encoded_mail())) match = SUBJECT_REQUEST_ID.search(email.subject) if match is not None: try: req = FoiRequest.objects.get(pk=match.group(1)) deferred.redeliver(req) except FoiRequest.DoesNotExist: continue redeliver_subject.short_description = _("Auto-Redeliver based on subject") def deliver_no_spam(self, request, queryset): for deferred in queryset: if deferred.request is not None: deferred.spam = False if deferred.delivered: deferred.save() else: deferred.redeliver(deferred.request) deliver_no_spam.short_description = _("Deliver and mark as no spam") def mark_as_spam(self, request, queryset): spam_senders = set() marked = 0 deleted = 0 for mes in queryset: if mes.sender in spam_senders: mes.delete() deleted += 1 continue mes.spam = True mes.save() spam_senders.add(mes.sender) marked += 1 self.message_user( request, _("Marked {marked} as spam, deleted {deleted} duplicates.").format( marked=marked, deleted=deleted)) mark_as_spam.short_description = _( "Mark as spam (delete all except one per sender)") def redeliver(self, request, queryset, auto=False): """ Redeliver undelivered mails """ opts = self.model._meta # Check that the user has change permission for the actual model if not self.has_change_permission(request): raise PermissionDenied Form = get_fk_form_class(self.model, 'request', self.admin_site) # User has already chosen the other req if request.POST.get('obj'): f = Form(request.POST) if f.is_valid(): req = f.cleaned_data['obj'] for deferred in queryset: deferred.redeliver(req) self.message_user(request, _("Successfully triggered redelivery.")) return None else: f = Form() context = { 'opts': opts, 'queryset': queryset, 'media': self.media, 'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME, 'form': f, 'applabel': opts.app_label } # Display the confirmation page return TemplateResponse(request, 'foirequest/admin_redeliver.html', context) redeliver.short_description = _("Redeliver to...")
class FoiMessageAdmin(admin.ModelAdmin): save_on_top = True list_display = ('subject', 'timestamp', 'sender_email', 'recipient_email', 'get_deliverystatus_display') list_filter = ( 'kind', 'is_response', 'sent', 'status', make_nullfilter('recipient_public_body', _('Has recipient public body')), 'deliverystatus__status', make_nullfilter('deliverystatus', _('Has delivery status')), 'sender_user__is_active', 'sender_user__is_blocked', 'sender_user__is_deleted', ) search_fields = ['subject', 'sender_email', 'recipient_email'] ordering = ('-timestamp', ) date_hierarchy = 'timestamp' raw_id_fields = ('request', 'sender_user', 'sender_public_body', 'recipient_public_body', 'original') inlines = [ DeliveryStatusInline, FoiAttachmentInline, ] actions = ['check_delivery_status', 'resend_messages'] def get_urls(self): urls = super(FoiMessageAdmin, self).get_urls() my_urls = [ url(r'^(?P<pk>\d+)/resend-message/$', self.admin_site.admin_view(self.resend_message), name='foirequest-foimessage-resend_message'), ] return my_urls + urls def get_queryset(self, request): qs = super(FoiMessageAdmin, self).get_queryset(request) qs = qs.select_related('deliverystatus') return qs def get_deliverystatus_display(self, obj): return obj.deliverystatus.get_status_display() get_deliverystatus_display.short_description = _('delivery status') def check_delivery_status(self, request, queryset): from .tasks import check_delivery_status for message in queryset: check_delivery_status.delay(message.id, extended=True) self.message_user( request, _("Selected messages are being checked for delivery.")) check_delivery_status.short_description = _("Check delivery status") def resend_message(self, request, pk): if not request.method == 'POST': raise PermissionDenied if not self.has_change_permission(request): raise PermissionDenied message = FoiMessage.objects.get(pk=pk, sent=False) message.force_resend() self.message_user(request, _('Message was send again.')) return redirect('admin:foirequest_foimessage_change', message.id) def resend_messages(self, request, queryset): if not request.method == 'POST': raise PermissionDenied if not self.has_change_permission(request): raise PermissionDenied count = 0 total = len(queryset) queryset = queryset.filter(sent=False) for message in queryset: message.force_resend() count += 1 self.message_user( request, _("{num} of {total} selected messages were sent.").format( num=count, total=total)) resend_message.short_description = _('Resend selected messages')
class PublicBodyBaseAdminMixin(ClassificationAssignMixin, PublicBodyReplacementMixin, AdminTagAllMixIn): form = PublicBodyAdminForm date_hierarchy = 'updated_at' prepopulated_fields = {"slug": ("name", )} save_on_top = True fieldsets = ( (None, { 'fields': ( 'name', 'slug', 'other_names', 'classification', 'url', 'email', 'fax', 'contact', 'address', ) }), (_('Context'), { 'fields': ( 'jurisdiction', 'laws', 'request_note', 'categories', 'description', 'file_index', 'org_chart', ), }), (_('Hierachy'), { 'classes': ('collapse', ), 'fields': ('parent', 'root', 'depth'), }), (_('Geo'), { 'classes': ('collapse', ), 'fields': ('regions', 'geo'), }), (_('Advanced'), { 'classes': ('collapse', ), 'fields': ( 'site', 'number_of_requests', 'website_dump', ), }), (_('Meta'), { 'fields': ('_created_by', 'created_at', '_updated_by', 'updated_at'), }), ) list_display = ('name', 'email', 'url', 'classification', 'jurisdiction', 'category_list', 'request_count') list_filter = ('jurisdiction', 'classification', 'categories', make_nullfilter('geo', _('Has geo coordinates')), make_nullfilter('regions', _('Has regions')), make_emptyfilter('email', 'E-Mail'), make_emptyfilter('fax', 'Fax')) filter_horizontal = ('laws', ) list_max_show_all = 5000 search_fields = ['name', 'other_names', 'description'] exclude = ('confirmed', ) raw_id_fields = ('parent', 'root', '_created_by', '_updated_by', 'regions', 'classification') tag_all_config = ('categories', CATEGORY_AUTOCOMPLETE_URL) readonly_fields = ('_created_by', 'created_at', '_updated_by', 'updated_at') actions = (ClassificationAssignMixin.actions + PublicBodyReplacementMixin.actions + [ 'export_csv', 'remove_from_index', 'tag_all', 'show_georegions', 'validate_publicbodies', ]) def get_queryset(self, request): qs = super(PublicBodyBaseAdminMixin, self).get_queryset(request) qs = qs.annotate(request_count=Count('foirequest')) qs = qs.select_related('classification', 'jurisdiction') return qs def request_count(self, obj): return obj.request_count request_count.admin_order_field = 'request_count' request_count.short_description = _('requests') def get_urls(self): urls = super(PublicBodyBaseAdminMixin, self).get_urls() my_urls = [ url(r'^import/$', self.admin_site.admin_view(self.import_csv), name='publicbody-publicbody-import_csv'), url(r'^geo-match/$', self.admin_site.admin_view(self.geo_match), name='publicbody-publicbody-geo_match'), ] return my_urls + urls def import_csv(self, request): if not request.method == 'POST': raise PermissionDenied if not self.has_change_permission(request): raise PermissionDenied importer = CSVImporter() url = request.POST.get('url') csv_file = request.FILES.get('file') try: if not url and not csv_file: raise ValueError(_('You need to provide a url or a file.')) if url: importer.import_from_url(url) else: importer.import_from_file(csv_file) except Exception as e: self.message_user(request, str(e)) else: self.message_user(request, _('Public Bodies were imported.')) return redirect('admin:publicbody_publicbody_changelist') def geo_match(self, request): from froide.georegion.models import GeoRegion if request.method == 'POST': if not self.has_change_permission(request): raise PermissionDenied data = json.loads(request.body) try: georegion = GeoRegion.objects.get(id=data['georegion']) except GeoRegion.DoesNotExist: return HttpResponse(status=404) try: pb = PublicBody.objects.get(id=data['publicbody']) except PublicBody.DoesNotExist: return HttpResponse(status=404) pb.regions.add(georegion) return HttpResponse(status=201, content=b'{}') opts = self.model._meta config = { 'url': { 'listCategories': reverse('api:category-list'), 'listClassifications': reverse('api:classification-list'), 'listPublicBodies': reverse('api:publicbody-list'), 'searchPublicBody': reverse('api:publicbody-search'), 'listGeoregion': reverse('api:georegion-list'), 'detailGeoregion': reverse('api:georegion-detail', kwargs={'pk': 0}), 'detailJurisdiction': reverse('api:jurisdiction-detail', kwargs={'pk': 0}), 'georegionAdminUrl': reverse('admin:georegion_georegion_change', kwargs={'object_id': 0}), 'publicbodyAdminUrl': reverse('admin:publicbody_publicbody_changelist'), 'publicbodyAdminChangeUrl': reverse('admin:publicbody_publicbody_change', kwargs={'object_id': 0}), 'publicbodyAddAdminUrl': reverse('admin:publicbody_publicbody_add'), } } ctx = { 'app_label': opts.app_label, 'opts': opts, 'config': json.dumps(config) } return render(request, 'publicbody/admin/match_georegions.html', ctx) def save_model(self, request, obj, form, change): obj._updated_by = request.user obj.updated_at = timezone.now() if change is None: obj._created_by = obj._updated_by obj.created_at = obj.updated_at super(PublicBodyBaseAdminMixin, self).save_model(request, obj, form, change) def category_list(self, obj): return ", ".join(o.name for o in obj.categories.all()) def export_csv(self, request, queryset): return export_csv_response(PublicBody.export_csv(queryset)) export_csv.short_description = _("Export to CSV") def remove_from_index(self, request, queryset): from django_elasticsearch_dsl.registries import registry for obj in queryset: registry.delete(obj, raise_on_error=False) self.message_user(request, _("Removed from search index")) remove_from_index.short_description = _("Remove from search index") def show_georegions(self, request, queryset): opts = self.model._meta context = { 'opts': opts, 'media': self.media, 'applabel': opts.app_label, 'no_regions': queryset.filter(regions=None), 'regions': json.dumps({ reg.id: pb.id for pb in queryset.exclude(regions=None) for reg in pb.regions.all() }) } # Display the confirmation page return TemplateResponse(request, 'publicbody/admin/show_georegions.html', context) show_georegions.short_description = _("Show georegions of") def validate_publicbodies(self, request, queryset): csv_stream = dict_to_csv_stream(validate_publicbodies(queryset)) return export_csv_response(csv_stream, name='validation.csv')
class MailingAdmin(admin.ModelAdmin): raw_id_fields = ('email_template', 'newsletter') list_display = ( 'name', 'email_template', 'created', 'newsletter', 'ready', 'sending_date', 'sending', 'sent', 'sent_percentage', 'publish' ) list_filter = ( 'ready', 'submitted', make_nullfilter('newsletter', 'Newsletter'), 'publish', 'sending', 'sent', ) search_fields = ('name',) actions = ['trigger_continue_sending'] def get_urls(self): urls = super().get_urls() my_urls = [ url( r'^(.+)/send/$', self.send, name='fds_mailing_mailing_send' ) ] return my_urls + urls def get_queryset(self, request): qs = super().get_queryset(request) qs = qs.annotate( total_recipients=models.Count('recipients'), sent_recipients=models.Count( 'recipients', filter=models.Q(recipients__sent__isnull=False) ), ) return qs.select_related('email_template', 'newsletter') def sent_percentage(self, obj): if obj.total_recipients == 0: return '-' return '{0:.2f}%'.format( obj.sent_recipients / obj.total_recipients * 100 ) def trigger_continue_sending(self, request, queryset): for mailing in queryset: continue_sending.delay(mailing.id) self.message_user( request, _('Continue sending selected mailings.'), level=messages.INFO ) trigger_continue_sending.short_description = _("Continue sending mailing") def send(self, request, object_id): if request.method != 'POST': raise PermissionDenied if not self.has_change_permission(request): raise PermissionDenied mailing = get_object_or_404(Mailing, id=object_id) change_url = reverse( 'admin:fds_mailing_mailing_change', args=[object_id] ) now = timezone.now() if mailing.sent or mailing.submitted: messages.error(request, _("Mailing already sent.")) return redirect(change_url) if mailing.sending_date and mailing.sending_date < now: messages.error(request, _("Mailing sending date in the past.")) return redirect(change_url) mailing.submitted = True if not mailing.sending_date: mailing.sending_date = timezone.now() mailing.save() transaction.on_commit(lambda: send_mailing.apply_async( (mailing.id, mailing.sending_date), eta=mailing.sending_date, retry=False )) messages.info(request, _("Your mailing is being sent.")) return redirect(change_url)
class ArticleAdmin(PlaceholderAdminMixin, admin.ModelAdmin): form = ArticleAdminForm date_hierarchy = 'start_publication' fieldsets = ( (_('Content'), { 'fields': ( 'title', 'status', 'teaser', 'image', ), }), (_('URL'), { 'fields': ('slug', 'start_publication'), 'description': _('Make sure these are correct before publication. Do not change after publication!' ) }), (_('Additional Content'), { 'fields': ( 'categories', 'tags', 'credits', ) }), (_('Templates'), { 'fields': ('content_template', 'detail_template'), 'classes': ('collapse', 'collapse-closed') }), (_('Publication Dates'), { 'fields': (('creation_date', 'end_publication', 'date_featured'), ), 'classes': ('collapse', 'collapse-closed') }), (_('Metadata'), { 'fields': ( 'excerpt', 'related', ), 'classes': ('collapse', 'collapse-closed') }), (_('Advanced'), { 'fields': ('language', 'uuid', 'sites'), 'classes': ('collapse', 'collapse-closed') }), ) add_fieldsets = ((None, { 'classes': ('wide', ), 'fields': ( 'title', 'slug', 'language', ) }), ) inlines = (AuthorshipInlineAdmin, ) list_display = ('get_title', 'get_edit_link', 'get_authors', 'get_categories', 'language', 'get_is_visible', 'has_translation', 'start_publication') list_filter = ( 'status', CategoryListFilter, make_nullfilter('categories', 'Hat Kategorie'), 'tags', 'date_featured', 'language', make_nullfilter('image', 'Hat Bild'), 'sites', 'creation_date', 'start_publication', 'end_publication', ) radio_fields = { 'content_template': admin.VERTICAL, 'detail_template': admin.VERTICAL } prepopulated_fields = {'slug': ('title', )} search_fields = ('title', 'excerpt', 'content', 'tags__name') raw_id_fields = ('related', ) filter_horizontal = ( 'categories', 'authors', ) save_on_top = True actions = ['set_language'] actions_on_top = True actions_on_bottom = True # def __init__(self, model, admin_site): # # self.form.admin_site = admin_site # super().__init__(model, admin_site) def get_queryset(self, request): qs = super().get_queryset(request) qs = qs.prefetch_related( 'categories', 'categories__translations', 'authors', 'authors__user', ) return qs def get_changeform_initial_data(self, request): """ Provide initial datas when creating an entry. """ get_data = super().get_changeform_initial_data(request) return get_data or { 'sites': [Site.objects.get_current().pk], 'authors': [request.user.pk] } def get_fieldsets(self, request, obj=None): if not obj: return self.add_fieldsets return super().get_fieldsets(request, obj) def save_model(self, request, article, form, change): """ Save the update time. """ if change: content = article.get_html_content(request) article.content = content or '' article.last_update = timezone.now() public_changed = False if 'status' in form.changed_data or 'start_publication' in form.changed_data: public_changed = True super().save_model(request, article, form, change) article.save() if public_changed: index_article(article) if not change: article.sites.add(Site.objects.get_current()) add_plugin(article.content_placeholder, 'TextPlugin', article.language, body='') def get_title(self, article): """ Return the title with word count and number of comments. """ return article.title get_title.short_description = _('title') def get_authors(self, article): """ Return the authors in HTML. """ return ', '.join(str(author) for author in article.authors.all()) get_authors.short_description = _('author(s)') def get_categories(self, article): """ Return the categories linked in HTML. """ categories = [category.title for category in article.categories.all()] return ', '.join(categories) get_categories.short_description = _('category(s)') def get_is_visible(self, article): """ Admin wrapper for article.is_visible. """ return article.is_visible get_is_visible.boolean = True get_is_visible.short_description = _('is visible') def get_edit_link(self, article): """ Return the authors in HTML. """ try: edit_link = article.get_absolute_edit_url() except NoReverseMatch: return _('unavailable for this site') return format_html('<a href="{url}" target="_blank">{title}</a>', url=edit_link + '?edit', title=_('Edit Content')) get_edit_link.short_description = _('Content') def has_translation(self, article): return bool(article.uuid) has_translation.short_description = _('Translated') has_translation.boolean = True def set_language(self, request, queryset): """ Set articles selected as published. """ Article.objects.mark_translations(queryset) self.message_user( request, _('The selected articles are now marked as translations of each other.' )) set_language.short_description = _( 'Set articles as translations of each other')
class FoiRequestAdmin(admin.ModelAdmin, AdminTagAllMixIn): form = FoiRequestAdminForm prepopulated_fields = {"slug": ("title",)} inlines = [ FoiMessageInline, ] list_display = ('title', 'first_message', 'secret_address', 'checked', 'public_body', 'status',) list_filter = ('jurisdiction', 'first_message', 'last_message', 'status', 'resolution', 'is_foi', 'checked', 'public', 'visibility', 'is_blocked', make_nullfilter('same_as', _(u'Has same request')), ('user', ForeignKeyFilter), ('public_body', ForeignKeyFilter), FoiRequestTagsFilter) search_fields = ['title', 'description', 'secret_address', 'reference'] ordering = ('-last_message',) date_hierarchy = 'first_message' autocomplete_resource_name = 'request' actions = ['mark_checked', 'mark_not_foi', 'tag_all', 'mark_same_as', 'remove_from_index', 'confirm_request', 'set_visible_to_user', 'unpublish'] raw_id_fields = ('same_as', 'public_body', 'user',) save_on_top = True def mark_checked(self, request, queryset): rows_updated = queryset.update(checked=True) self.message_user(request, _("%d request(s) successfully marked as checked." % rows_updated)) mark_checked.short_description = _("Mark selected requests as checked") def mark_not_foi(self, request, queryset): rows_updated = queryset.update(is_foi=False) self.message_user(request, _("%d request(s) successfully marked as not FoI." % rows_updated)) mark_not_foi.short_description = _("Mark selected requests as not FoI") def mark_same_as(self, request, queryset): """ Mark selected requests as same as the one we are choosing now. """ opts = self.model._meta # Check that the user has change permission for the actual model if not self.has_change_permission(request): raise PermissionDenied # User has already chosen the other req if request.POST.get('req_id'): try: req = self.model.objects.get(id=int(request.POST.get('req_id'))) except (ValueError, self.model.DoesNotExist,): raise PermissionDenied queryset.update(same_as=req) count_same_foirequests.delay(req.id) self.message_user(request, _("Successfully marked requests as identical.")) # Return None to display the change list page again. return None db = router.db_for_write(self.model) context = { 'opts': opts, 'queryset': queryset, 'media': self.media, 'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME, 'req_widget': mark_safe(admin.widgets.ForeignKeyRawIdWidget( self.model._meta.get_field( 'same_as').rel, self.admin_site, using=db).render( 'req_id', None, {'id': 'id_req_id'}) .replace('../../..', '../..')), 'applabel': opts.app_label } # Display the confirmation page return TemplateResponse(request, 'foirequest/admin_mark_same_as.html', context) mark_same_as.short_description = _("Mark selected requests as identical to...") def remove_from_index(self, request, queryset): from haystack import connections as haystack_connections for obj in queryset: for using in haystack_connections.connections_info.keys(): backend = haystack_connections[using].get_backend() backend.remove(obj) self.message_user(request, _("Removed from search index")) remove_from_index.short_description = _("Remove from search index") def confirm_request(self, request, queryset): foireq = queryset[0] if foireq.status != 'awaiting_user_confirmation': self.message_user(request, _("Request not in correct state!")) return None self.message_user(request, _("Message send successfully!")) FoiRequest.confirmed_request(foireq.user, foireq.pk) return None confirm_request.short_description = _("Confirm request if unconfirmed") def set_visible_to_user(self, request, queryset): queryset.update(visibility=1) self.message_user(request, _("Selected requests are now only visible to requester.")) set_visible_to_user.short_description = _("Set only visible to requester") def unpublish(self, request, queryset): queryset.update(public=False) self.message_user(request, _("Selected requests are now unpublished.")) unpublish.short_description = _("Unpublish")
class DeferredMessageAdmin(admin.ModelAdmin): model = DeferredMessage list_filter = (make_nullfilter('request', _(u'Has request')), 'spam') search_fields = ['recipient'] date_hierarchy = 'timestamp' ordering = ('-timestamp',) list_display = ('recipient', 'timestamp', 'request', 'spam') raw_id_fields = ('request',) actions = ['redeliver', 'auto_redeliver'] save_on_top = True def auto_redeliver(self, request, queryset): parser = EmailParser() for deferred in queryset: email = parser.parse(BytesIO(deferred.decoded_mail())) match = SUBJECT_REQUEST_ID.search(email['subject']) if match is not None: try: req = FoiRequest.objects.get(pk=match.group(1)) deferred.redeliver(req) except FoiRequest.DoesNotExist: continue auto_redeliver.short_description = _("Auto-Redeliver based on subject") def redeliver(self, request, queryset, auto=False): """ Redeliver undelivered mails """ opts = self.model._meta # Check that the user has change permission for the actual model if not self.has_change_permission(request): raise PermissionDenied # User has already chosen the other req if request.POST.get('req_id'): req_id = int(request.POST.get('req_id')) try: req = FoiRequest.objects.get(id=req_id) except (ValueError, FoiRequest.DoesNotExist,): raise PermissionDenied for deferred in queryset: deferred.redeliver(req) self.message_user(request, _("Successfully triggered redelivery.")) return None db = router.db_for_write(self.model) context = { 'opts': opts, 'queryset': queryset, 'media': self.media, 'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME, 'req_widget': mark_safe(admin.widgets.ForeignKeyRawIdWidget( self.model._meta.get_field( 'request').rel, self.admin_site, using=db).render( 'req_id', None, {'id': 'id_req_id'}) .replace('../../..', '../..')), 'applabel': opts.app_label } # Display the confirmation page return TemplateResponse(request, 'foirequest/admin_redeliver.html', context) redeliver.short_description = _("Redeliver to...")
class PublicBodyBaseAdminMixin(ClassificationAssignMixin, AdminTagAllMixIn): form = PublicBodyAdminForm date_hierarchy = 'updated_at' prepopulated_fields = {"slug": ("name", )} save_on_top = True fieldsets = ( (None, { 'fields': ( 'name', 'slug', 'other_names', 'classification', 'url', 'email', 'fax', 'contact', 'address', ) }), (_('Context'), { 'fields': ( 'jurisdiction', 'laws', 'request_note', 'categories', 'description', 'file_index', 'org_chart', ), }), (_('Hierachy'), { 'classes': ('collapse', ), 'fields': ('parent', 'root', 'depth'), }), (_('Geo'), { 'classes': ('collapse', ), 'fields': ('regions', 'geo'), }), (_('Advanced'), { 'classes': ('collapse', ), 'fields': ('site', 'number_of_requests', 'website_dump', 'wikidata_item'), }), (_('Meta'), { 'fields': ('_created_by', 'created_at', '_updated_by', 'updated_at'), }), ) list_display = ('name', 'email', 'url', 'classification', 'jurisdiction', 'category_list') list_filter = ('jurisdiction', 'classification', 'categories', make_nullfilter('geo', _('Has geo coordinates')), make_nullfilter('regions', _('Has regions')), make_emptyfilter('email', 'E-Mail'), make_emptyfilter('fax', 'Fax')) filter_horizontal = ('laws', ) list_max_show_all = 5000 search_fields = ['name', 'other_names', 'description'] exclude = ('confirmed', ) raw_id_fields = ('parent', 'root', '_created_by', '_updated_by', 'regions', 'classification') tag_all_config = ('categories', CATEGORY_AUTOCOMPLETE_URL) readonly_fields = ('_created_by', 'created_at', '_updated_by', 'updated_at') actions = ClassificationAssignMixin.actions + [ 'export_csv', 'remove_from_index', 'tag_all', 'show_georegions' ] def get_queryset(self, request): qs = super(PublicBodyBaseAdminMixin, self).get_queryset(request) qs = qs.select_related('classification', 'jurisdiction') return qs def get_urls(self): urls = super(PublicBodyBaseAdminMixin, self).get_urls() my_urls = [ url(r'^import/$', self.admin_site.admin_view(self.import_csv), name='publicbody-publicbody-import_csv'), ] return my_urls + urls def import_csv(self, request): if not request.method == 'POST': raise PermissionDenied if not self.has_change_permission(request): raise PermissionDenied importer = CSVImporter() url = request.POST.get('url') csv_file = request.FILES.get('file') try: if not url and not csv_file: raise ValueError(_('You need to provide a url or a file.')) if url: importer.import_from_url(url) else: importer.import_from_file(csv_file) except Exception as e: self.message_user(request, str(e)) else: self.message_user(request, _('Public Bodies were imported.')) return redirect('admin:publicbody_publicbody_changelist') def save_model(self, request, obj, form, change): obj._updated_by = request.user obj.updated_at = timezone.now() if change is None: obj._created_by = obj._updated_by obj.created_at = obj.updated_at super(PublicBodyBaseAdminMixin, self).save_model(request, obj, form, change) def category_list(self, obj): return ", ".join(o.name for o in obj.categories.all()) def export_csv(self, request, queryset): return export_csv_response(PublicBody.export_csv(queryset)) export_csv.short_description = _("Export to CSV") def remove_from_index(self, request, queryset): from django_elasticsearch_dsl.registries import registry for obj in queryset: registry.delete(obj, raise_on_error=False) self.message_user(request, _("Removed from search index")) remove_from_index.short_description = _("Remove from search index") def show_georegions(self, request, queryset): opts = self.model._meta queryset = queryset context = { 'opts': opts, 'media': self.media, 'applabel': opts.app_label, 'no_regions': queryset.filter(regions=None), 'region_string': ','.join([ str(reg.id) for pb in queryset.exclude(regions=None) for reg in pb.regions.all() ]) } # Display the confirmation page return TemplateResponse(request, 'publicbody/admin/show_georegions.html', context) show_georegions.short_description = _("Show georegions of")
class FoiRequestAdmin(admin.ModelAdmin, AdminTagAllMixIn): form = FoiRequestAdminForm prepopulated_fields = {"slug": ("title", )} inlines = [ FoiMessageInline, ] list_display = ('title', 'first_message', 'secret_address', 'request_page', 'public_body', 'status', 'visibility') list_filter = ('jurisdiction', 'first_message', 'last_message', 'status', 'resolution', 'is_foi', 'checked', 'public', 'visibility', 'is_blocked', make_nullfilter('same_as', _('Has same request')), ('user', ForeignKeyFilter), ('public_body', ForeignKeyFilter), ('project', ForeignKeyFilter), FoiRequestTagsFilter) search_fields = ['title', 'description', 'secret_address', 'reference'] ordering = ('-last_message', ) date_hierarchy = 'first_message' tag_all_config = ('tags', reverse_lazy('api:request-tags-autocomplete')) actions = [ 'mark_checked', 'mark_not_foi', 'tag_all', 'mark_same_as', 'remove_from_index', 'confirm_request', 'set_visible_to_user', 'unpublish' ] raw_id_fields = ('same_as', 'public_body', 'user', 'project') save_on_top = True def request_page(self, obj): return format_html('<a href="{}">{}</a>', obj.get_absolute_url(), _('request page')) request_page.allow_tags = True def mark_checked(self, request, queryset): rows_updated = queryset.update(checked=True) self.message_user( request, _("%d request(s) successfully marked as checked." % rows_updated)) mark_checked.short_description = _("Mark selected requests as checked") def mark_not_foi(self, request, queryset): rows_updated = queryset.update(is_foi=False) self.message_user( request, _("%d request(s) successfully marked as not FoI." % rows_updated)) mark_not_foi.short_description = _("Mark selected requests as not FoI") def mark_same_as(self, request, queryset): """ Mark selected requests as same as the one we are choosing now. """ opts = self.model._meta # Check that the user has change permission for the actual model if not self.has_change_permission(request): raise PermissionDenied Form = get_fk_form_class(self.model, 'same_as', self.admin_site) # User has already chosen the other req if request.POST.get('obj'): f = Form(request.POST) if f.is_valid(): req = f.cleaned_data['obj'] queryset.update(same_as=req) count_same_foirequests.delay(req.id) self.message_user( request, _("Successfully marked requests as identical.")) # Return None to display the change list page again. return None else: f = Form() context = { 'opts': opts, 'queryset': queryset, 'media': self.media, 'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME, 'form': f, 'applabel': opts.app_label } # Display the confirmation page return TemplateResponse(request, 'foirequest/admin_mark_same_as.html', context) mark_same_as.short_description = _( "Mark selected requests as identical to...") def remove_from_index(self, request, queryset): from haystack import connections as haystack_connections for obj in queryset: for using in haystack_connections.connections_info.keys(): backend = haystack_connections[using].get_backend() backend.remove(obj) self.message_user(request, _("Removed from search index")) remove_from_index.short_description = _("Remove from search index") def confirm_request(self, request, queryset): foireq = queryset[0] if foireq.status != 'awaiting_user_confirmation': self.message_user(request, _("Request not in correct state!")) return None self.message_user(request, _("Message send successfully!")) FoiRequest.confirmed_request(foireq.user, foireq.pk) return None confirm_request.short_description = _("Confirm request if unconfirmed") def set_visible_to_user(self, request, queryset): queryset.update(visibility=1) self.message_user( request, _("Selected requests are now only visible to requester.")) set_visible_to_user.short_description = _("Set only visible to requester") def unpublish(self, request, queryset): queryset.update(public=False) self.message_user(request, _("Selected requests are now unpublished.")) unpublish.short_description = _("Unpublish")
class FoiMessageAdmin(GuidanceSelectionMixin, admin.ModelAdmin): save_on_top = True list_display = ('subject', 'timestamp', 'message_page', 'sender_email', 'recipient_email', 'is_response', 'kind', 'get_deliverystatus_display') list_filter = ( 'kind', 'is_response', 'sent', 'status', 'deliverystatus__status', make_nullfilter('deliverystatus', _('Has delivery status')), 'sender_user__is_active', 'sender_user__is_blocked', 'sender_user__is_deleted', MessageTagsFilter, ('request__reference', SearchFilter), ('sender_public_body', ForeignKeyFilter), ('recipient_public_body', ForeignKeyFilter), ('request__user', ForeignKeyFilter), make_nullfilter('foiattachment_set', _('Has attachments')), ) search_fields = ['subject', 'sender_email', 'recipient_email'] ordering = ('-timestamp', ) date_hierarchy = 'timestamp' raw_id_fields = ('request', 'sender_user', 'sender_public_body', 'recipient_public_body', 'original') inlines = [ DeliveryStatusInline, FoiAttachmentInline, ] actions = [ 'check_delivery_status', 'resend_messages', 'run_guidance', 'run_guidance_notify', 'attach_guidance_action' ] def get_urls(self): urls = super(FoiMessageAdmin, self).get_urls() my_urls = [ url(r'^(?P<pk>\d+)/resend-message/$', self.admin_site.admin_view(self.resend_message), name='foirequest-foimessage-resend_message'), ] return my_urls + urls def get_queryset(self, request): qs = super(FoiMessageAdmin, self).get_queryset(request) qs = qs.select_related('deliverystatus') return qs def message_page(self, obj): return format_html('<a href="{}">{}</a>', obj.get_absolute_short_url(), _('on site')) def attach_guidance_action(self, request, queryset): ''' Magic from GuidanceSelectionMixin''' return self._assign_action_handler('', 'attach_guidance_action', request, queryset) attach_guidance_action.short_description = _( 'Add guidance action to messages') def run_guidance_notify(self, request, queryset): self._run_guidance(queryset, notify=True) self.message_user( request, _("Guidance is being run against selected messages. Users are notified." )) run_guidance_notify.short_description = _( "Run guidance with user notifications") def run_guidance(self, request, queryset): self._run_guidance(queryset, notify=False) self.message_user( request, _("Guidance is being run against selected messages.")) run_guidance.short_description = _("Run guidance") def _run_guidance(self, queryset, notify=False): from froide.guide.tasks import run_guidance_on_queryset_task message_ids = queryset.values_list('id', flat=True) run_guidance_on_queryset_task.delay(message_ids, notify=notify) def get_deliverystatus_display(self, obj): return obj.deliverystatus.get_status_display() get_deliverystatus_display.short_description = _('delivery status') def check_delivery_status(self, request, queryset): from .tasks import check_delivery_status for message in queryset: check_delivery_status.delay(message.id, extended=True) self.message_user( request, _("Selected messages are being checked for delivery.")) check_delivery_status.short_description = _("Check delivery status") def resend_message(self, request, pk): if not request.method == 'POST': raise PermissionDenied if not self.has_change_permission(request): raise PermissionDenied message = FoiMessage.objects.get(pk=pk, sent=False) message.request.is_blocked = False message.request.save() message.request.user.is_blocked = False message.request.user.save() message.force_resend() self.message_user(request, _('Message was send again.')) return redirect('admin:foirequest_foimessage_change', message.id) def resend_messages(self, request, queryset): if not request.method == 'POST': raise PermissionDenied if not self.has_change_permission(request): raise PermissionDenied count = 0 total = len(queryset) queryset = queryset.filter(sent=False).select_related('request') for message in queryset: message.request.is_blocked = False message.request.save() message.request.user.is_blocked = False message.request.user.save() message.timestamp = timezone.now() message.force_resend() count += 1 self.message_user( request, _("{num} of {total} selected messages were sent.").format( num=count, total=total)) resend_message.short_description = _('Resend selected messages')
class InformationObjectAdmin(admin.ModelAdmin): list_display = ( 'title', 'ident', 'campaign', 'publicbody', 'request_count' ) list_filter = ( 'campaign', 'foirequest__status', 'foirequest__resolution', 'resolved', make_nullfilter('foirequest', _('Has request')), make_nullfilter('documents', _('Has documents')), make_nullfilter('publicbody', _('Has public body')), make_nullfilter('geo', _('Has geo')) ) raw_id_fields = ( 'publicbody', 'foirequest', 'foirequests', 'documents' ) search_fields = ('title', 'ident') actions = [ 'clean_requests', 'resolve_requests', 'export_csv', 'update_search_index' ] def get_queryset(self, request): qs = super().get_queryset(request) qs = qs.annotate( request_count=Count('foirequests') ) qs = qs.select_related('publicbody') return qs def request_count(self, obj): return obj.request_count request_count.admin_order_field = 'request_count' request_count.short_description = _('requests') def get_urls(self): urls = super(InformationObjectAdmin, self).get_urls() my_urls = [ url(r'^upload/$', self.admin_site.admin_view(self.upload_information_objects), name='froide_campaign-admin_upload'), ] return my_urls + urls def export_csv(self, request, queryset): queryset = queryset.select_related('foirequest', 'publicbody') csv_generator = InformationObject.objects.export_csv(queryset) return export_csv_response(csv_generator) export_csv.short_description = _("Export to CSV") def update_search_index(self, request, queryset): InformationObject.objects.update_search_index(qs=queryset) def upload_information_objects(self, request): if not request.method == 'POST': raise PermissionDenied if not self.has_change_permission(request): raise PermissionDenied importer = CSVImporter() csv_file = request.FILES['file'] file = csv_file.read().decode('utf-8') io_string = io.StringIO(file) io_string.seek(0) reader = csv.DictReader(io_string) importer.run(reader) return redirect('admin:froide_campaign_informationobject_changelist') def resolve_requests(self, request, queryset): queryset = queryset.filter(foirequest__isnull=False) queryset = queryset.select_related('foirequest') for iobj in queryset: if iobj.foirequest is None: continue iobj.foirequest.status = 'resolved' iobj.foirequest.resolution = 'successful' iobj.foirequest.save() return None resolve_requests.short_description = _("Mark requests as " "successfully resolved") def clean_requests(self, request, queryset): queryset = queryset.filter(foirequest__isnull=False) queryset = queryset.select_related('foirequest') a_day_ago = timezone.now() - timedelta(days=1) for iobj in queryset: if iobj.foirequest is None: continue if iobj.foirequest.first_message >= a_day_ago: continue if iobj.foirequest.is_visible(): continue iobj.foirequest = None iobj.save() return None clean_requests.short_description = _("Clean out bad requests")
class FoiAttachmentAdmin(admin.ModelAdmin): raw_id_fields = ('belongs_to', 'redacted', 'converted', 'document') ordering = ('-id', ) date_hierarchy = 'timestamp' list_display = ( 'name', 'filetype', 'size', 'admin_link_message', 'approved', 'can_approve', ) list_filter = ( 'can_approve', 'approved', 'is_redacted', 'is_converted', make_nullfilter('redacted', _('Has redacted version')), make_nullfilter('converted', _('Has converted version')), 'filetype', 'pending', ('belongs_to__request', ForeignKeyFilter), ('belongs_to__request__user', ForeignKeyFilter), ) search_fields = ['name'] formfield_overrides = { models.FileField: { 'widget': AttachmentFileWidget }, } actions = [ 'approve', 'disapprove', 'cannot_approve', 'convert', 'ocr_attachment', 'make_document' ] def get_queryset(self, request): qs = super().get_queryset(request) qs = qs.select_related('belongs_to') return qs def admin_link_message(self, obj): return format_html( '<a href="{}">{}</a>', reverse('admin:foirequest_foimessage_change', args=(obj.belongs_to_id, )), _('See FoiMessage')) def approve(self, request, queryset): rows_updated = queryset.update(approved=True) self.message_user( request, _("%d attachment(s) successfully approved." % rows_updated)) approve.short_description = _("Mark selected as approved") def disapprove(self, request, queryset): rows_updated = queryset.update(approved=False) self.message_user( request, _("%d attachment(s) successfully disapproved." % rows_updated)) disapprove.short_description = _("Mark selected as disapproved") def cannot_approve(self, request, queryset): rows_updated = queryset.update(can_approve=False, approved=False) self.message_user( request, _("%d attachment(s) successfully marked as not approvable/approved." % rows_updated)) cannot_approve.short_description = _( "Mark selected as not approvable/approved") def convert(self, request, queryset): if not queryset: return count = 0 for instance in queryset: if instance.can_convert_to_pdf(): count += 1 convert_attachment_task.delay(instance.pk) self.message_user(request, _("Conversion tasks started: %s") % count) convert.short_description = _("Convert to PDF") def make_document(self, request, queryset): count = 0 for instance in queryset: doc = instance.create_document() if doc: count += 1 self.message_user(request, _("%s document(s) created") % count) make_document.short_description = _("Make into document") def ocr_attachment(self, request, queryset): for att in queryset: ocr_pdf_attachment(att) ocr_attachment.short_description = _('OCR PDF')
class VenueRequestAdmin(LeafletGeoAdmin): display_raw = True raw_id_fields = ("amenity", ) inlines = [VenueRequestItemInlineAdmin] date_hierarchy = "last_request" list_filter = ( "last_status", "last_resolution", make_nullfilter("last_request", "Hat Anfrage"), make_nullfilter("geo", "Hat eigene Koordinate"), make_nullfilter("amenity", "Hat OSM-Betrieb"), "amenity__category", ) list_display = ( "name", "last_request", "get_last_status_display", "get_last_resolution_display", "get_provider", ) search_fields = ("name", "ident") actions = ["merge_venues"] def get_urls(self): urls = super(VenueRequestAdmin, self).get_urls() my_urls = [ url( r"^export-users/$", self.admin_site.admin_view(self.export_users), name="froide_food-admin-export_users", ), ] return my_urls + urls def export_users(self, request): if not request.method == "GET": raise PermissionDenied if not request.user.is_superuser: raise PermissionDenied four_months_ago = timezone.now() - timedelta(days=4 * 31) queryset = User.objects.filter( is_active=True, tags__in=UserTag.objects.filter(slug="food-first"), date_joined__gte=four_months_ago, ) fields = ("id", "first_name", "last_name", "email", "date_joined") stream = User.export_csv(queryset, fields=fields) return export_csv_response(stream) def get_last_status_display(self, obj): return FoiRequest.STATUS_RESOLUTION_DICT.get(obj.last_status, (obj.last_status, ))[0] get_last_status_display.short_description = "letzter Status" def get_provider(self, obj): return obj.ident.split(":")[0] get_provider.short_description = "provider" def get_last_resolution_display(self, obj): return FoiRequest.STATUS_RESOLUTION_DICT.get( obj.last_resolution, (obj.last_resolution, ))[0] get_last_resolution_display.short_description = "letztes Ergebnis" def merge_venues(self, request, queryset): """ Set articles selected as published. """ from .utils import merge_venues merge_venues(queryset) def save_model(self, request, obj, form, change): from .utils import check_and_merge_venue if not obj.ident.startswith("amenity:") and obj.amenity: # Setting amenity on venue request obj.ident = "amenity:%s" % obj.amenity.ident obj.update_from_provider() check_and_merge_venue(obj) super().save_model(request, obj, form, change) merge_venues.short_description = "Merge venues"
class DeferredMessageAdmin(admin.ModelAdmin): model = DeferredMessage list_filter = ('delivered', make_nullfilter('request', _('Has request')), 'spam') search_fields = ( 'recipient', 'sender', ) date_hierarchy = 'timestamp' ordering = ('-timestamp', ) list_display = ( 'recipient', 'timestamp', 'spam', 'delivered', 'sender', 'request_last_message', 'request_status', 'request_page', ) raw_id_fields = ('request', ) actions = [ 'mark_as_spam', 'deliver_no_spam', 'redeliver', 'redeliver_subject', 'close_request' ] save_on_top = True def get_queryset(self, request): qs = super().get_queryset(request) qs = qs.select_related('request') return qs def request_last_message(self, obj): if obj.request: return obj.request.last_message def request_status(self, obj): if obj.request: return obj.request.get_status_display() def request_page(self, obj): if obj.request: return format_html('<a href="{}">{}</a>', obj.request.get_absolute_url(), obj.request.title) def close_request(self, request, queryset): for mes in queryset: mes.request.closed = True mes.request.save() return None close_request.short_description = _('Close associated requests') def redeliver_subject(self, request, queryset): parser = EmailParser() for deferred in queryset: email = parser.parse(BytesIO(deferred.encoded_mail())) match = SUBJECT_REQUEST_ID.search(email.subject) if match is not None: try: req = FoiRequest.objects.get(pk=match.group(1)) deferred.redeliver(req) except FoiRequest.DoesNotExist: continue redeliver_subject.short_description = _("Auto-Redeliver based on subject") def deliver_no_spam(self, request, queryset): for deferred in queryset: if deferred.request is not None: deferred.spam = False if deferred.delivered: deferred.save() else: deferred.redeliver(deferred.request) deliver_no_spam.short_description = _("Deliver and mark as no spam") def mark_as_spam(self, request, queryset): spam_senders = set() marked = 0 deleted = 0 for mes in queryset: if mes.sender in spam_senders: mes.delete() deleted += 1 continue mes.spam = True mes.save() spam_senders.add(mes.sender) marked += 1 self.message_user( request, _("Marked {marked} as spam, deleted {deleted} duplicates.").format( marked=marked, deleted=deleted)) mark_as_spam.short_description = _( "Mark as spam (delete all except one per sender)") redeliver = make_choose_object_action(FoiRequest, execute_redeliver, _("Redeliver to..."))