Ejemplo n.º 1
0
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")
Ejemplo n.º 2
0
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")
Ejemplo n.º 3
0
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')
Ejemplo n.º 4
0
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")
Ejemplo n.º 5
0
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'
Ejemplo n.º 6
0
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"
Ejemplo n.º 7
0
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', )
Ejemplo n.º 8
0
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
Ejemplo n.º 9
0
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
Ejemplo n.º 10
0
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
Ejemplo n.º 11
0
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...")
Ejemplo n.º 12
0
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'
Ejemplo n.º 13
0
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...")
Ejemplo n.º 14
0
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...")
Ejemplo n.º 15
0
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')
Ejemplo n.º 16
0
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')
Ejemplo n.º 17
0
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)
Ejemplo n.º 18
0
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')
Ejemplo n.º 19
0
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")
Ejemplo n.º 20
0
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...")
Ejemplo n.º 21
0
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")
Ejemplo n.º 22
0
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")
Ejemplo n.º 23
0
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')
Ejemplo n.º 24
0
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")
Ejemplo n.º 25
0
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')
Ejemplo n.º 26
0
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"
Ejemplo n.º 27
0
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..."))