From c3a144f2ca9badbbf608d9d757b79ebdf4ae51d2 Mon Sep 17 00:00:00 2001 From: Jonas Winkler Date: Fri, 6 Jul 2018 13:25:02 +0200 Subject: [PATCH 1/5] inbox tags, archive tags, archive serial number for documents --- .../commands/document_correspondents.py | 2 +- .../management/commands/document_retagger.py | 2 +- .../migrations/0022_workflow_improvements.py | 34 +++++++++++++++++++ src/documents/models.py | 15 ++++++++ src/documents/signals/handlers.py | 2 +- 5 files changed, 52 insertions(+), 3 deletions(-) mode change 100644 => 100755 src/documents/management/commands/document_correspondents.py mode change 100644 => 100755 src/documents/management/commands/document_retagger.py create mode 100755 src/documents/migrations/0022_workflow_improvements.py mode change 100644 => 100755 src/documents/signals/handlers.py diff --git a/src/documents/management/commands/document_correspondents.py b/src/documents/management/commands/document_correspondents.py old mode 100644 new mode 100755 index 0709c49d2..d3b324bb1 --- a/src/documents/management/commands/document_correspondents.py +++ b/src/documents/management/commands/document_correspondents.py @@ -41,7 +41,7 @@ class Command(Renderable, BaseCommand): self.verbosity = options["verbosity"] - for document in Document.objects.filter(correspondent__isnull=True): + for document in Document.objects.filter(correspondent__isnull=True).exclude(tags__is_archived_tag=True): potential_correspondents = list( Correspondent.match_all(document.content)) diff --git a/src/documents/management/commands/document_retagger.py b/src/documents/management/commands/document_retagger.py old mode 100644 new mode 100755 index 8f56e1eea..d3fd83962 --- a/src/documents/management/commands/document_retagger.py +++ b/src/documents/management/commands/document_retagger.py @@ -22,7 +22,7 @@ class Command(Renderable, BaseCommand): self.verbosity = options["verbosity"] - for document in Document.objects.all(): + for document in Document.objects.all().exclude(tags__is_archived_tag=True): tags = Tag.objects.exclude( pk__in=document.tags.values_list("pk", flat=True)) diff --git a/src/documents/migrations/0022_workflow_improvements.py b/src/documents/migrations/0022_workflow_improvements.py new file mode 100755 index 000000000..534cb78e7 --- /dev/null +++ b/src/documents/migrations/0022_workflow_improvements.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.10 on 2018-02-04 13:07 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('documents', '0021_document_storage_type'), + ] + + operations = [ + + migrations.AddField( + model_name='document', + name='archive_serial_number', + field=models.IntegerField(unique=True, blank=True, null=True, db_index=True), + ), + + migrations.AddField( + model_name='tag', + name='is_inbox_tag', + field=models.BooleanField(default=False), + ), + + migrations.AddField( + model_name='tag', + name='is_archived_tag', + field=models.BooleanField(default=False), + ), + + ] diff --git a/src/documents/models.py b/src/documents/models.py index 780cddb0a..2da28da57 100755 --- a/src/documents/models.py +++ b/src/documents/models.py @@ -180,6 +180,14 @@ class Tag(MatchingModel): colour = models.PositiveIntegerField(choices=COLOURS, default=1) + is_inbox_tag = models.BooleanField( + default=False, + help_text="Marks this tag as an inbox tag: All newly consumed documents will be tagged with inbox tags.") + + is_archived_tag = models.BooleanField( + default=False, + help_text="Marks this tag as an archive tag: All documents tagged with archive tags will never be modified automatically (i.e., modifying tags by matching rules)") + class Document(models.Model): @@ -247,6 +255,13 @@ class Document(models.Model): added = models.DateTimeField( default=timezone.now, editable=False, db_index=True) + archive_serial_number = models.IntegerField( + blank=True, + null=True, + unique=True, + db_index=True, + help_text="The position of this document in your physical document archive.") + class Meta: ordering = ("correspondent", "title") diff --git a/src/documents/signals/handlers.py b/src/documents/signals/handlers.py old mode 100644 new mode 100755 index cdeaaba40..b3579b567 --- a/src/documents/signals/handlers.py +++ b/src/documents/signals/handlers.py @@ -47,7 +47,7 @@ def set_correspondent(sender, document=None, logging_group=None, **kwargs): def set_tags(sender, document=None, logging_group=None, **kwargs): current_tags = set(document.tags.all()) - relevant_tags = set(Tag.match_all(document.content)) - current_tags + relevant_tags = (set(Tag.match_all(document.content)) | set(Tag.objects.filter(is_inbox_tag=True))) - current_tags if not relevant_tags: return From e143a20f5048ce2f170cdbf7b2485c701f70badb Mon Sep 17 00:00:00 2001 From: Jonas Winkler Date: Fri, 6 Jul 2018 13:51:50 +0200 Subject: [PATCH 2/5] automatically update documents whenever a tag or correspondent is changed (this should make the document_retagger and document_correspondent managers somewhat obsolete (?) --- src/documents/admin.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/documents/admin.py b/src/documents/admin.py index 6a32edc9b..ca0017f59 100755 --- a/src/documents/admin.py +++ b/src/documents/admin.py @@ -110,6 +110,13 @@ class CorrespondentAdmin(CommonAdmin): list_filter = ("matching_algorithm",) list_editable = ("match", "matching_algorithm") + def save_model(self, request, obj, form, change): + super().save_model(request, obj, form, change) + + for document in Document.objects.filter(correspondent__isnull=True).exclude(tags__is_archived_tag=True): + if obj.matches(document.content): + document.correspondent = obj + document.save(update_fields=("correspondent",)) class TagAdmin(CommonAdmin): @@ -117,6 +124,13 @@ class TagAdmin(CommonAdmin): list_filter = ("colour", "matching_algorithm") list_editable = ("colour", "match", "matching_algorithm") + def save_model(self, request, obj, form, change): + super().save_model(request, obj, form, change) + + for document in Document.objects.all().exclude(tags__is_archived_tag=True): + if obj.matches(document.content): + document.tags.add(obj) + class DocumentAdmin(CommonAdmin): From c5b315f5188ab2ea6589cb9bb47401f112980f31 Mon Sep 17 00:00:00 2001 From: Jonas Winkler Date: Fri, 6 Jul 2018 18:04:31 +0200 Subject: [PATCH 3/5] Show document serial number on change list --- src/documents/admin.py | 2 +- .../document/change_list_results.html | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) mode change 100644 => 100755 src/documents/templates/admin/documents/document/change_list_results.html diff --git a/src/documents/admin.py b/src/documents/admin.py index ca0017f59..cc7c27524 100755 --- a/src/documents/admin.py +++ b/src/documents/admin.py @@ -142,7 +142,7 @@ class DocumentAdmin(CommonAdmin): search_fields = ("correspondent__name", "title", "content", "tags__name") readonly_fields = ("added",) list_display = ("title", "created", "added", "thumbnail", "correspondent", - "tags_") + "tags_", "archive_serial_number") list_filter = ("tags", "correspondent", FinancialYearFilter, MonthListFilter) diff --git a/src/documents/templates/admin/documents/document/change_list_results.html b/src/documents/templates/admin/documents/document/change_list_results.html old mode 100644 new mode 100755 index b33cd2927..d295ce601 --- a/src/documents/templates/admin/documents/document/change_list_results.html +++ b/src/documents/templates/admin/documents/document/change_list_results.html @@ -25,6 +25,7 @@ border-radius: 2%; overflow: hidden; height: 300px; + position: relative; } .result .header { padding: 5px; @@ -79,6 +80,15 @@ .result .image img { width: 100%; } + .result .footer { + position: absolute; + bottom: 0; + right: 0; + border-left: 1px solid #cccccc; + border-top: 1px solid #cccccc; + padding: 4px 10px 4px 10px; + background: white; + } .grid { margin-right: 260px; @@ -152,7 +162,8 @@ {# 4: Image #} {# 5: Correspondent #} {# 6: Tags #} - {# 7: Document edit url #} + {# 7: Archive serial number #} + {# 8: Document edit url #}
@@ -166,7 +177,7 @@ selection would not be possible with mouse click + drag. Instead, the underlying link would be dragged. {% endcomment %} - +
{{ result.0 }}
{{ result.5 }} @@ -178,6 +189,9 @@
{{ result.2 }}
{{ result.4 }}
+ {# Only show the archive serial number if it is set on the document. #} + {# checking for >-< (i.e., will a dash be displayed) doesn't feel like a very good solution to me. #} + {% if '>-<' not in result.7 %}{% endif %}
{% endfor %} From a40737bd0e0284c29522195979765c97bad25ac2 Mon Sep 17 00:00:00 2001 From: Jonas Winkler Date: Tue, 10 Jul 2018 15:39:24 +0200 Subject: [PATCH 4/5] Added actions to modify tags and correspondents on multiple documents --- src/documents/admin.py | 164 +++++++++++++++++- .../documents/document/mass_modify_tag.html | 46 +++++ .../documents/document/set_correspondent.html | 46 +++++ 3 files changed, 255 insertions(+), 1 deletion(-) create mode 100755 src/documents/templates/admin/documents/document/mass_modify_tag.html create mode 100755 src/documents/templates/admin/documents/document/set_correspondent.html diff --git a/src/documents/admin.py b/src/documents/admin.py index cc7c27524..cb1cbc281 100755 --- a/src/documents/admin.py +++ b/src/documents/admin.py @@ -1,8 +1,12 @@ from datetime import datetime from django.conf import settings -from django.contrib import admin +from django.contrib import admin, messages +from django.contrib.admin import helpers +from django.contrib.admin.utils import model_ngettext from django.contrib.auth.models import User, Group +from django.core.exceptions import PermissionDenied +from django.template.response import TemplateResponse from django.urls import reverse from django.templatetags.static import static from django.utils.safestring import mark_safe @@ -132,11 +136,166 @@ class TagAdmin(CommonAdmin): document.tags.add(obj) +def add_tag_to_selected(modeladmin, request, queryset): + opts = modeladmin.model._meta + app_label = opts.app_label + + if not modeladmin.has_change_permission(request): + raise PermissionDenied + + if request.POST.get('post'): + n = queryset.count() + tag = Tag.objects.get(id=request.POST.get('tag_id')) + if n: + for obj in queryset: + obj.tags.add(tag) + obj_display = str(obj) + modeladmin.log_change(request, obj, obj_display) + modeladmin.message_user(request, "Successfully added tag %(tag)s to %(count)d %(items)s." % { + "tag": tag.name, "count": n, "items": model_ngettext(modeladmin.opts, n) + }, messages.SUCCESS) + + # Return None to display the change list page again. + return None + + title = "Add tag to multiple documents" + + context = dict( + modeladmin.admin_site.each_context(request), + title=title, + queryset=queryset, + opts=opts, + action_checkbox_name=helpers.ACTION_CHECKBOX_NAME, + media=modeladmin.media, + action="add_tag_to_selected", + tags=Tag.objects.all() + ) + + request.current_app = modeladmin.admin_site.name + + return TemplateResponse(request, + "admin/%s/%s/mass_modify_tag.html" % (app_label, opts.model_name) + , context) + + +add_tag_to_selected.short_description = "Add tag to selected documents" + + +def remove_tag_from_selected(modeladmin, request, queryset): + opts = modeladmin.model._meta + app_label = opts.app_label + + if not modeladmin.has_change_permission(request): + raise PermissionDenied + + if request.POST.get('post'): + n = queryset.count() + tag = Tag.objects.get(id=request.POST.get('tag_id')) + if n: + for obj in queryset: + obj.tags.remove(tag) + obj_display = str(obj) + modeladmin.log_change(request, obj, obj_display) + modeladmin.message_user(request, "Successfully removed tag %(tag)s from %(count)d %(items)s." % { + "tag": tag.name, "count": n, "items": model_ngettext(modeladmin.opts, n) + }, messages.SUCCESS) + + # Return None to display the change list page again. + return None + + title = "Remove tag from multiple documents" + + context = dict( + modeladmin.admin_site.each_context(request), + title=title, + queryset=queryset, + opts=opts, + action_checkbox_name=helpers.ACTION_CHECKBOX_NAME, + media=modeladmin.media, + action="remove_tag_from_selected", + tags=Tag.objects.all() + ) + + request.current_app = modeladmin.admin_site.name + + return TemplateResponse(request, + "admin/%s/%s/mass_modify_tag.html" % (app_label, opts.model_name) + , context) + + +remove_tag_from_selected.short_description = "Remove tag from selected documents" + + +def set_correspondent_on_selected(modeladmin, request, queryset): + opts = modeladmin.model._meta + app_label = opts.app_label + + if not modeladmin.has_change_permission(request): + raise PermissionDenied + + if request.POST.get('post'): + n = queryset.count() + correspondent = Correspondent.objects.get(id=request.POST.get('correspondent_id')) + if n: + for obj in queryset: + obj_display = str(obj) + modeladmin.log_change(request, obj, obj_display) + queryset.update(correspondent=correspondent) + modeladmin.message_user(request, "Successfully set correspondent %(correspondent)s on %(count)d %(items)s." % { + "correspondent": correspondent.name, "count": n, "items": model_ngettext(modeladmin.opts, n) + }, messages.SUCCESS) + + # Return None to display the change list page again. + return None + + title = "Set correspondent on multiple documents" + + context = dict( + modeladmin.admin_site.each_context(request), + title=title, + queryset=queryset, + opts=opts, + action_checkbox_name=helpers.ACTION_CHECKBOX_NAME, + media=modeladmin.media, + correspondents=Correspondent.objects.all() + ) + + request.current_app = modeladmin.admin_site.name + + return TemplateResponse(request, + "admin/%s/%s/set_correspondent.html" % (app_label, opts.model_name) + , context) + + +set_correspondent_on_selected.short_description = "Set correspondent on selected documents" + + +def remove_correspondent_from_selected(modeladmin, request, queryset): + if not modeladmin.has_change_permission(request): + raise PermissionDenied + + n = queryset.count() + if n: + for obj in queryset: + obj_display = str(obj) + modeladmin.log_change(request, obj, obj_display) + queryset.update(correspondent=None) + modeladmin.message_user(request, "Successfully removed correspondent from %(count)d %(items)s." % { + "count": n, "items": model_ngettext(modeladmin.opts, n) + }, messages.SUCCESS) + + return None + + +remove_correspondent_from_selected.short_description = "Remove correspondent from selected documents" + + class DocumentAdmin(CommonAdmin): class Media: css = { "all": ("paperless.css",) + } search_fields = ("correspondent__name", "title", "content", "tags__name") @@ -148,6 +307,9 @@ class DocumentAdmin(CommonAdmin): ordering = ["-created", "correspondent"] + + actions = [add_tag_to_selected, remove_tag_from_selected, set_correspondent_on_selected, remove_correspondent_from_selected] + def has_add_permission(self, request): return False diff --git a/src/documents/templates/admin/documents/document/mass_modify_tag.html b/src/documents/templates/admin/documents/document/mass_modify_tag.html new file mode 100755 index 000000000..71ef09ae8 --- /dev/null +++ b/src/documents/templates/admin/documents/document/mass_modify_tag.html @@ -0,0 +1,46 @@ +{% extends "admin/base_site.html" %} +{% load i18n l10n admin_urls static %} +{% load staticfiles %} + +{% block extrahead %} +{{ block.super }} +{{ media }} + + +{% endblock %} + +{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation delete-selected-confirmation{% endblock %} + +{% block breadcrumbs %} + +{% endblock %} + +{% block content %} +

Please select the tag.

+
{% csrf_token %} +
+ {% for obj in queryset %} + + {% endfor %} +

+ +

+ + + +

+ + {% trans "Go back" %} +

+
+
+{% endblock %} \ No newline at end of file diff --git a/src/documents/templates/admin/documents/document/set_correspondent.html b/src/documents/templates/admin/documents/document/set_correspondent.html new file mode 100755 index 000000000..22993421e --- /dev/null +++ b/src/documents/templates/admin/documents/document/set_correspondent.html @@ -0,0 +1,46 @@ +{% extends "admin/base_site.html" %} +{% load i18n l10n admin_urls static %} +{% load staticfiles %} + +{% block extrahead %} +{{ block.super }} +{{ media }} + + +{% endblock %} + +{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation delete-selected-confirmation{% endblock %} + +{% block breadcrumbs %} + +{% endblock %} + +{% block content %} +

Please select the correspondent.

+
{% csrf_token %} +
+ {% for obj in queryset %} + + {% endfor %} +

+ +

+ + + +

+ + {% trans "Go back" %} +

+
+
+{% endblock %} \ No newline at end of file From ba9d7c88925801c4dd2e951caa642f2702e83a9f Mon Sep 17 00:00:00 2001 From: Jonas Winkler Date: Wed, 11 Jul 2018 11:56:53 +0200 Subject: [PATCH 5/5] Moved actions to separate file --- src/documents/actions.py | 161 +++++++++++++++++ src/documents/admin.py | 163 +----------------- .../admin/documents/document/change_form.html | 0 3 files changed, 164 insertions(+), 160 deletions(-) create mode 100755 src/documents/actions.py mode change 100644 => 100755 src/documents/templates/admin/documents/document/change_form.html diff --git a/src/documents/actions.py b/src/documents/actions.py new file mode 100755 index 000000000..7f23baabd --- /dev/null +++ b/src/documents/actions.py @@ -0,0 +1,161 @@ +from django.contrib import messages +from django.contrib.admin import helpers +from django.contrib.admin.utils import model_ngettext +from django.core.exceptions import PermissionDenied +from django.template.response import TemplateResponse + +from documents.models import Tag, Correspondent + + +def add_tag_to_selected(modeladmin, request, queryset): + opts = modeladmin.model._meta + app_label = opts.app_label + + if not modeladmin.has_change_permission(request): + raise PermissionDenied + + if request.POST.get('post'): + n = queryset.count() + tag = Tag.objects.get(id=request.POST.get('tag_id')) + if n: + for obj in queryset: + obj.tags.add(tag) + obj_display = str(obj) + modeladmin.log_change(request, obj, obj_display) + modeladmin.message_user(request, "Successfully added tag %(tag)s to %(count)d %(items)s." % { + "tag": tag.name, "count": n, "items": model_ngettext(modeladmin.opts, n) + }, messages.SUCCESS) + + # Return None to display the change list page again. + return None + + title = "Add tag to multiple documents" + + context = dict( + modeladmin.admin_site.each_context(request), + title=title, + queryset=queryset, + opts=opts, + action_checkbox_name=helpers.ACTION_CHECKBOX_NAME, + media=modeladmin.media, + action="add_tag_to_selected", + tags=Tag.objects.all() + ) + + request.current_app = modeladmin.admin_site.name + + return TemplateResponse(request, + "admin/%s/%s/mass_modify_tag.html" % (app_label, opts.model_name) + , context) + + +add_tag_to_selected.short_description = "Add tag to selected documents" + + +def remove_tag_from_selected(modeladmin, request, queryset): + opts = modeladmin.model._meta + app_label = opts.app_label + + if not modeladmin.has_change_permission(request): + raise PermissionDenied + + if request.POST.get('post'): + n = queryset.count() + tag = Tag.objects.get(id=request.POST.get('tag_id')) + if n: + for obj in queryset: + obj.tags.remove(tag) + obj_display = str(obj) + modeladmin.log_change(request, obj, obj_display) + modeladmin.message_user(request, "Successfully removed tag %(tag)s from %(count)d %(items)s." % { + "tag": tag.name, "count": n, "items": model_ngettext(modeladmin.opts, n) + }, messages.SUCCESS) + + # Return None to display the change list page again. + return None + + title = "Remove tag from multiple documents" + + context = dict( + modeladmin.admin_site.each_context(request), + title=title, + queryset=queryset, + opts=opts, + action_checkbox_name=helpers.ACTION_CHECKBOX_NAME, + media=modeladmin.media, + action="remove_tag_from_selected", + tags=Tag.objects.all() + ) + + request.current_app = modeladmin.admin_site.name + + return TemplateResponse(request, + "admin/%s/%s/mass_modify_tag.html" % (app_label, opts.model_name) + , context) + + +remove_tag_from_selected.short_description = "Remove tag from selected documents" + + +def set_correspondent_on_selected(modeladmin, request, queryset): + opts = modeladmin.model._meta + app_label = opts.app_label + + if not modeladmin.has_change_permission(request): + raise PermissionDenied + + if request.POST.get('post'): + n = queryset.count() + correspondent = Correspondent.objects.get(id=request.POST.get('correspondent_id')) + if n: + for obj in queryset: + obj_display = str(obj) + modeladmin.log_change(request, obj, obj_display) + queryset.update(correspondent=correspondent) + modeladmin.message_user(request, "Successfully set correspondent %(correspondent)s on %(count)d %(items)s." % { + "correspondent": correspondent.name, "count": n, "items": model_ngettext(modeladmin.opts, n) + }, messages.SUCCESS) + + # Return None to display the change list page again. + return None + + title = "Set correspondent on multiple documents" + + context = dict( + modeladmin.admin_site.each_context(request), + title=title, + queryset=queryset, + opts=opts, + action_checkbox_name=helpers.ACTION_CHECKBOX_NAME, + media=modeladmin.media, + correspondents=Correspondent.objects.all() + ) + + request.current_app = modeladmin.admin_site.name + + return TemplateResponse(request, + "admin/%s/%s/set_correspondent.html" % (app_label, opts.model_name) + , context) + + +set_correspondent_on_selected.short_description = "Set correspondent on selected documents" + + +def remove_correspondent_from_selected(modeladmin, request, queryset): + if not modeladmin.has_change_permission(request): + raise PermissionDenied + + n = queryset.count() + if n: + for obj in queryset: + obj_display = str(obj) + modeladmin.log_change(request, obj, obj_display) + queryset.update(correspondent=None) + modeladmin.message_user(request, "Successfully removed correspondent from %(count)d %(items)s." % { + "count": n, "items": model_ngettext(modeladmin.opts, n) + }, messages.SUCCESS) + + return None + + +remove_correspondent_from_selected.short_description = "Remove correspondent from selected documents" diff --git a/src/documents/admin.py b/src/documents/admin.py index a2fb6cf8b..a85b087a3 100755 --- a/src/documents/admin.py +++ b/src/documents/admin.py @@ -1,16 +1,14 @@ from datetime import datetime from django.conf import settings -from django.contrib import admin, messages -from django.contrib.admin import helpers -from django.contrib.admin.utils import model_ngettext +from django.contrib import admin from django.contrib.auth.models import User, Group -from django.core.exceptions import PermissionDenied -from django.template.response import TemplateResponse from django.urls import reverse from django.templatetags.static import static from django.utils.safestring import mark_safe +from documents.actions import add_tag_to_selected, remove_tag_from_selected, set_correspondent_on_selected, \ + remove_correspondent_from_selected from .models import Correspondent, Tag, Document, Log @@ -144,160 +142,6 @@ class TagAdmin(CommonAdmin): return obj.documents.count() -def add_tag_to_selected(modeladmin, request, queryset): - opts = modeladmin.model._meta - app_label = opts.app_label - - if not modeladmin.has_change_permission(request): - raise PermissionDenied - - if request.POST.get('post'): - n = queryset.count() - tag = Tag.objects.get(id=request.POST.get('tag_id')) - if n: - for obj in queryset: - obj.tags.add(tag) - obj_display = str(obj) - modeladmin.log_change(request, obj, obj_display) - modeladmin.message_user(request, "Successfully added tag %(tag)s to %(count)d %(items)s." % { - "tag": tag.name, "count": n, "items": model_ngettext(modeladmin.opts, n) - }, messages.SUCCESS) - - # Return None to display the change list page again. - return None - - title = "Add tag to multiple documents" - - context = dict( - modeladmin.admin_site.each_context(request), - title=title, - queryset=queryset, - opts=opts, - action_checkbox_name=helpers.ACTION_CHECKBOX_NAME, - media=modeladmin.media, - action="add_tag_to_selected", - tags=Tag.objects.all() - ) - - request.current_app = modeladmin.admin_site.name - - return TemplateResponse(request, - "admin/%s/%s/mass_modify_tag.html" % (app_label, opts.model_name) - , context) - - -add_tag_to_selected.short_description = "Add tag to selected documents" - - -def remove_tag_from_selected(modeladmin, request, queryset): - opts = modeladmin.model._meta - app_label = opts.app_label - - if not modeladmin.has_change_permission(request): - raise PermissionDenied - - if request.POST.get('post'): - n = queryset.count() - tag = Tag.objects.get(id=request.POST.get('tag_id')) - if n: - for obj in queryset: - obj.tags.remove(tag) - obj_display = str(obj) - modeladmin.log_change(request, obj, obj_display) - modeladmin.message_user(request, "Successfully removed tag %(tag)s from %(count)d %(items)s." % { - "tag": tag.name, "count": n, "items": model_ngettext(modeladmin.opts, n) - }, messages.SUCCESS) - - # Return None to display the change list page again. - return None - - title = "Remove tag from multiple documents" - - context = dict( - modeladmin.admin_site.each_context(request), - title=title, - queryset=queryset, - opts=opts, - action_checkbox_name=helpers.ACTION_CHECKBOX_NAME, - media=modeladmin.media, - action="remove_tag_from_selected", - tags=Tag.objects.all() - ) - - request.current_app = modeladmin.admin_site.name - - return TemplateResponse(request, - "admin/%s/%s/mass_modify_tag.html" % (app_label, opts.model_name) - , context) - - -remove_tag_from_selected.short_description = "Remove tag from selected documents" - - -def set_correspondent_on_selected(modeladmin, request, queryset): - opts = modeladmin.model._meta - app_label = opts.app_label - - if not modeladmin.has_change_permission(request): - raise PermissionDenied - - if request.POST.get('post'): - n = queryset.count() - correspondent = Correspondent.objects.get(id=request.POST.get('correspondent_id')) - if n: - for obj in queryset: - obj_display = str(obj) - modeladmin.log_change(request, obj, obj_display) - queryset.update(correspondent=correspondent) - modeladmin.message_user(request, "Successfully set correspondent %(correspondent)s on %(count)d %(items)s." % { - "correspondent": correspondent.name, "count": n, "items": model_ngettext(modeladmin.opts, n) - }, messages.SUCCESS) - - # Return None to display the change list page again. - return None - - title = "Set correspondent on multiple documents" - - context = dict( - modeladmin.admin_site.each_context(request), - title=title, - queryset=queryset, - opts=opts, - action_checkbox_name=helpers.ACTION_CHECKBOX_NAME, - media=modeladmin.media, - correspondents=Correspondent.objects.all() - ) - - request.current_app = modeladmin.admin_site.name - - return TemplateResponse(request, - "admin/%s/%s/set_correspondent.html" % (app_label, opts.model_name) - , context) - - -set_correspondent_on_selected.short_description = "Set correspondent on selected documents" - - -def remove_correspondent_from_selected(modeladmin, request, queryset): - if not modeladmin.has_change_permission(request): - raise PermissionDenied - - n = queryset.count() - if n: - for obj in queryset: - obj_display = str(obj) - modeladmin.log_change(request, obj, obj_display) - queryset.update(correspondent=None) - modeladmin.message_user(request, "Successfully removed correspondent from %(count)d %(items)s." % { - "count": n, "items": model_ngettext(modeladmin.opts, n) - }, messages.SUCCESS) - - return None - - -remove_correspondent_from_selected.short_description = "Remove correspondent from selected documents" - - class DocumentAdmin(CommonAdmin): class Media: @@ -315,7 +159,6 @@ class DocumentAdmin(CommonAdmin): ordering = ["-created", "correspondent"] - actions = [add_tag_to_selected, remove_tag_from_selected, set_correspondent_on_selected, remove_correspondent_from_selected] def has_add_permission(self, request): diff --git a/src/documents/templates/admin/documents/document/change_form.html b/src/documents/templates/admin/documents/document/change_form.html old mode 100644 new mode 100755