Removed a bunch of stuff thats not needed anymore with the new gui.

This commit is contained in:
Jonas Winkler 2020-10-17 01:57:08 +02:00
parent 421dab786d
commit da5b7d3104
28 changed files with 17 additions and 56779 deletions

3
.gitignore vendored
View File

@ -74,6 +74,7 @@ db.sqlite3-journal
# Other stuff that doesn't belong
.virtualenv
virtualenv
/venv
docker-compose.yml
docker-compose.env
@ -86,3 +87,5 @@ static/
# Classification Models
models/
/paperless.conf

View File

@ -34,7 +34,7 @@ jinja2==2.10.1
langdetect==1.0.7
markupsafe==1.1.1
more-itertools==7.2.0
numpy==1.15.1
numpy==1.19.2
packaging==19.1
pdftotext==2.1.1
pillow==5.4.1
@ -54,8 +54,8 @@ pytest-sugar==0.9.2
pytest-xdist==1.26.0
pytest==4.1.1
python-dateutil==2.7.5
scikit-learn==0.19.2
scipy==1.1.0
scikit-learn==0.23-2
scipy==1.5.2
python-dotenv==0.10.1
python-gnupg==0.4.4
python-levenshtein==0.12.0

View File

@ -1,204 +0,0 @@
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.classifier import DocumentClassifier
from documents.models import Correspondent, DocumentType, Tag
def select_action(
modeladmin, request, queryset, title, action, modelclass,
success_message="", document_action=None, queryset_action=None):
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()
selected_object = modelclass.objects.get(id=request.POST.get("obj_id"))
if n:
for document in queryset:
if document_action:
document_action(document, selected_object)
document_display = str(document)
modeladmin.log_change(request, document, document_display)
if queryset_action:
queryset_action(queryset, selected_object)
modeladmin.message_user(request, success_message % {
"selected_object": selected_object.name,
"count": n,
"items": model_ngettext(modeladmin.opts, n)
}, messages.SUCCESS)
# Return None to display the change list page again.
return None
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=action,
objects=modelclass.objects.all(),
itemname=model_ngettext(modelclass, 1)
)
request.current_app = modeladmin.admin_site.name
return TemplateResponse(
request,
"admin/{}/{}/select_object.html".format(app_label, opts.model_name),
context
)
def simple_action(
modeladmin, request, queryset, success_message="",
document_action=None, queryset_action=None):
if not modeladmin.has_change_permission(request):
raise PermissionDenied
n = queryset.count()
if n:
for document in queryset:
if document_action:
document_action(document)
document_display = str(document)
modeladmin.log_change(request, document, document_display)
if queryset_action:
queryset_action(queryset)
modeladmin.message_user(request, success_message % {
"count": n, "items": model_ngettext(modeladmin.opts, n)
}, messages.SUCCESS)
# Return None to display the change list page again.
return None
def add_tag_to_selected(modeladmin, request, queryset):
return select_action(
modeladmin=modeladmin,
request=request,
queryset=queryset,
title="Add tag to multiple documents",
action="add_tag_to_selected",
modelclass=Tag,
success_message="Successfully added tag %(selected_object)s to "
"%(count)d %(items)s.",
document_action=lambda doc, tag: doc.tags.add(tag)
)
def remove_tag_from_selected(modeladmin, request, queryset):
return select_action(
modeladmin=modeladmin,
request=request,
queryset=queryset,
title="Remove tag from multiple documents",
action="remove_tag_from_selected",
modelclass=Tag,
success_message="Successfully removed tag %(selected_object)s from "
"%(count)d %(items)s.",
document_action=lambda doc, tag: doc.tags.remove(tag)
)
def set_correspondent_on_selected(modeladmin, request, queryset):
return select_action(
modeladmin=modeladmin,
request=request,
queryset=queryset,
title="Set correspondent on multiple documents",
action="set_correspondent_on_selected",
modelclass=Correspondent,
success_message="Successfully set correspondent %(selected_object)s "
"on %(count)d %(items)s.",
queryset_action=lambda qs, corr: qs.update(correspondent=corr)
)
def remove_correspondent_from_selected(modeladmin, request, queryset):
return simple_action(
modeladmin=modeladmin,
request=request,
queryset=queryset,
success_message="Successfully removed correspondent from %(count)d "
"%(items)s.",
queryset_action=lambda qs: qs.update(correspondent=None)
)
def set_document_type_on_selected(modeladmin, request, queryset):
return select_action(
modeladmin=modeladmin,
request=request,
queryset=queryset,
title="Set document type on multiple documents",
action="set_document_type_on_selected",
modelclass=DocumentType,
success_message="Successfully set document type %(selected_object)s "
"on %(count)d %(items)s.",
queryset_action=lambda qs, document_type: qs.update(
document_type=document_type)
)
def remove_document_type_from_selected(modeladmin, request, queryset):
return simple_action(
modeladmin=modeladmin,
request=request,
queryset=queryset,
success_message="Successfully removed document type from %(count)d "
"%(items)s.",
queryset_action=lambda qs: qs.update(document_type=None)
)
def run_document_classifier_on_selected(modeladmin, request, queryset):
clf = DocumentClassifier()
try:
clf.reload()
return simple_action(
modeladmin=modeladmin,
request=request,
queryset=queryset,
success_message="Successfully applied document classifier to "
"%(count)d %(items)s.",
document_action=lambda doc: clf.classify_document(
doc,
classify_correspondent=True,
classify_tags=True,
classify_document_type=True)
)
except FileNotFoundError:
modeladmin.message_user(
request,
"Classifier model file not found.",
messages.ERROR
)
return None
add_tag_to_selected.short_description = "Add tag to selected documents"
remove_tag_from_selected.short_description = \
"Remove tag from selected documents"
set_correspondent_on_selected.short_description = \
"Set correspondent on selected documents"
remove_correspondent_from_selected.short_description = \
"Remove correspondent from selected documents"
set_document_type_on_selected.short_description = \
"Set document type on selected documents"
remove_document_type_from_selected.short_description = \
"Remove document type from selected documents"
run_document_classifier_on_selected.short_description = \
"Run document classifier on selected"

View File

@ -13,16 +13,6 @@ from django.utils.http import urlquote
from django.utils.safestring import mark_safe
from djangoql.admin import DjangoQLSearchMixin
from documents.actions import (
add_tag_to_selected,
remove_correspondent_from_selected,
remove_tag_from_selected,
set_correspondent_on_selected,
set_document_type_on_selected,
remove_document_type_from_selected,
run_document_classifier_on_selected
)
from .models import Correspondent, Document, DocumentType, Log, Tag
@ -91,27 +81,6 @@ class FinancialYearFilter(admin.SimpleListFilter):
created__lte=self._fy_end(end))
class RecentCorrespondentFilter(admin.RelatedFieldListFilter):
"""
If PAPERLESS_RECENT_CORRESPONDENT_YEARS is set, we limit the available
correspondents to documents sent our way over the past ``n`` years.
"""
def field_choices(self, field, request, model_admin):
years = settings.PAPERLESS_RECENT_CORRESPONDENT_YEARS
correspondents = Correspondent.objects.all()
if years and years > 0:
self.title = "Correspondent (Recent)"
days = 365 * years
correspondents = correspondents.filter(
documents__created__gte=datetime.now() - timedelta(days=days)
).distinct()
return [(c.id, c.name) for c in correspondents]
class CommonAdmin(admin.ModelAdmin):
list_per_page = settings.PAPERLESS_LIST_PER_PAGE
@ -196,12 +165,12 @@ class DocumentAdmin(DjangoQLSearchMixin, CommonAdmin):
search_fields = ("correspondent__name", "title", "content", "tags__name")
readonly_fields = ("added", "file_type", "storage_type",)
list_display = ("title", "created", "added", "thumbnail", "correspondent",
list_display = ("title", "created", "added", "correspondent",
"tags_", "archive_serial_number", "document_type")
list_filter = (
"document_type",
"tags",
("correspondent", RecentCorrespondentFilter),
"correspondent",
FinancialYearFilter
)
@ -209,22 +178,8 @@ class DocumentAdmin(DjangoQLSearchMixin, CommonAdmin):
ordering = ["-created", "correspondent"]
actions = [
add_tag_to_selected,
remove_tag_from_selected,
set_correspondent_on_selected,
remove_correspondent_from_selected,
set_document_type_on_selected,
remove_document_type_from_selected,
run_document_classifier_on_selected
]
date_hierarchy = "created"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.document_queue = []
def has_add_permission(self, request):
return False
@ -232,81 +187,6 @@ class DocumentAdmin(DjangoQLSearchMixin, CommonAdmin):
return obj.created.date().strftime("%Y-%m-%d")
created_.short_description = "Created"
def changelist_view(self, request, extra_context=None):
response = super().changelist_view(
request,
extra_context=extra_context
)
if request.method == "GET":
cl = self.get_changelist_instance(request)
self.document_queue = [doc.id for doc in cl.queryset]
return response
def change_view(self, request, object_id=None, form_url='',
extra_context=None):
extra_context = extra_context or {}
doc = Document.objects.get(id=object_id)
extra_context["download_url"] = doc.download_url
extra_context["file_type"] = doc.file_type
if self.document_queue and object_id:
if int(object_id) in self.document_queue:
# There is a queue of documents
current_index = self.document_queue.index(int(object_id))
if current_index < len(self.document_queue) - 1:
# ... and there are still documents in the queue
extra_context["next_object"] = self.document_queue[
current_index + 1
]
return super(DocumentAdmin, self).change_view(
request,
object_id,
form_url,
extra_context=extra_context,
)
def response_change(self, request, obj):
# This is mostly copied from ModelAdmin.response_change()
opts = self.model._meta
preserved_filters = self.get_preserved_filters(request)
msg_dict = {
"name": opts.verbose_name,
"obj": format_html(
'<a href="{}">{}</a>',
urlquote(request.path),
obj
),
}
if "_saveandeditnext" in request.POST:
msg = format_html(
'The {name} "{obj}" was changed successfully. '
'Editing next object.',
**msg_dict
)
self.message_user(request, msg, messages.SUCCESS)
redirect_url = reverse(
"admin:{}_{}_change".format(opts.app_label, opts.model_name),
args=(request.POST["_next_object"],),
current_app=self.admin_site.name
)
redirect_url = add_preserved_filters(
{
"preserved_filters": preserved_filters,
"opts": opts
},
redirect_url
)
return HttpResponseRedirect(redirect_url)
return super().response_change(request, obj)
@mark_safe
def thumbnail(self, obj):
return self._html_tag(
@ -314,7 +194,7 @@ class DocumentAdmin(DjangoQLSearchMixin, CommonAdmin):
self._html_tag(
"img",
src=reverse("fetch", kwargs={"kind": "thumb", "pk": obj.pk}),
width=180,
height=100,
alt="Thumbnail of {}".format(obj.file_name),
title=obj.file_name
),
@ -327,16 +207,8 @@ class DocumentAdmin(DjangoQLSearchMixin, CommonAdmin):
for tag in obj.tags.all():
colour = tag.get_colour_display()
r += self._html_tag(
"a",
tag.slug,
**{
"class": "tag",
"style": "background-color: {};".format(colour),
"href": "{}?tags__id__exact={}".format(
reverse("admin:documents_document_changelist"),
tag.pk
)
}
"span",
tag.slug + ", "
)
return r
@ -381,5 +253,5 @@ admin.site.register(Log, LogAdmin)
# Unless we implement multi-user, these default registrations don't make sense.
admin.site.unregister(Group)
admin.site.unregister(User)
#admin.site.unregister(Group)
#admin.site.unregister(User)

View File

@ -1 +0,0 @@
image.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

View File

@ -1 +0,0 @@
image.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -1 +0,0 @@
image.png

View File

@ -1 +0,0 @@
image.png

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,66 +0,0 @@
// The following jQuery snippet will add a small square next to the selection
// drop-down on the `Add tag` page that will update to show the selected tag
// color as the drop-down value is changed.
django.jQuery(document).ready(function(){
if (django.jQuery("#id_colour").length) {
let colour;
let colour_num;
colour_num = django.jQuery("#id_colour").val() - 1;
colour = django.jQuery('#id_colour')[0][colour_num].text;
django.jQuery('#id_colour').after('<div class="colour_square"></div>');
django.jQuery('.colour_square').css({
'float': 'left',
'width': '20px',
'height': '20px',
'margin': '5px',
'border': '1px solid rgba(0, 0, 0, .2)',
'background': colour
});
django.jQuery('#id_colour').change(function () {
colour_num = django.jQuery("#id_colour").val() - 1;
colour = django.jQuery('#id_colour')[0][colour_num].text;
django.jQuery('.colour_square').css({'background': colour});
});
} else if (django.jQuery("select[id*='colour']").length) {
django.jQuery('select[id*="-colour"]').each(function (index, element) {
let id;
let loop_colour_num;
let loop_colour;
id = "colour_square_" + index;
django.jQuery(element).after('<div class="colour_square" id="' + id + '"></div>');
loop_colour_num = django.jQuery(element).val() - 1;
loop_colour = django.jQuery(element)[0][loop_colour_num].text;
django.jQuery("<style type='text/css'>\
.colour_square{ \
float: left; \
width: 20px; \
height: 20px; \
margin: 5px; \
border: 1px solid rgba(0,0,0,.2); \
} </style>").appendTo("head");
django.jQuery('#' + id).css({'background': loop_colour});
console.log(id, loop_colour_num, loop_colour);
django.jQuery(element).change(function () {
loop_colour_num = django.jQuery(element).val() - 1;
loop_colour = django.jQuery(element)[0][loop_colour_num].text;
django.jQuery('#' + id).css({'background': loop_colour});
console.log('#' + id, loop_colour)
});
})
}
});

View File

@ -1,36 +0,0 @@
th.column-document,
td.field-document {
text-align: center;
}
td a.tag {
padding: 0 0.5em;
color: #ffffff;
border-radius: 0.2em;
margin: 1px;
display: inline-block;
}
#result_list th.column-note {
text-align: right;
}
#result_list td.field-note {
text-align: right;
}
#result_list td textarea {
width: 90%;
height: 5em;
}
#change_form_twocolumn_parent {
display: flex;
}
#change_form_form_parent {
flex:50%;
margin-right: 10px;
}
#change_form_viewer_parent {
flex:50%;
margin-left: 10px;
text-align: center;
}

View File

@ -1,93 +0,0 @@
{% extends 'admin/base_site.html' %}
{# NOTE: This should probably be extending base.html. See CSS comment below details. #}
{% load static %}
{% load custom_css from customisation %}
{% load custom_js from customisation %}
{% block extrahead %}
<link rel="icon" type="image/x-icon" href="{% url 'favicon' %}" />
<style>
#header {
background-color: #90a9b7;
line-height: inherit;
height: auto;
}
#branding h1 {
font-weight: inherit;
font-size: inherit;
}
.button,
.button:active,
.button:focus,
.button:hover,
a.button,
.submit-row input,
input[type="submit"],
input[type="submit"]:active,
input[type="submit"]:focus,
input[type="submit"]:hover,
input[type="button"],
input[type="button"]:active,
input[type="button"]:focus,
input[type="button"]:hover {
background-color: #074f57;
}
.module h2,
.module caption,
.inline-group h2 {
background-color: #90a9b7;
}
div.breadcrumbs {
background-color: #077187;
}
.module h2,
.module caption,
.inline-group h2 {
background-color: #077187;
}
</style>
{% endblock %}
{% block branding %}
<h1 id="site-name">
<a href="{% url 'admin:index' %}"><img src="{% static 'paperless/img/logo-light.png' %}" alt="Paperless" /></a>
</h1>
{% endblock %}
{% block blockbots %}
{% comment %}
This really should be extending `extrastyle`, but the the
django-flat-responsive package decided that it wanted to put its CSS in
this block, so to make sure that overrides are in fact overriding
everything else, we have to do the Wrong Thing here.
Once we switch to Django 2.x and drop django-flat-responsive, we should
switch this to `extrastyle` where it should be.
{% endcomment %}
{{ block.super }}
{% custom_css %}
{% endblock blockbots %}
{% block footer %}
{% comment %}
The Django admin doesn't have a block for Javascript you'd want placed in
the footer, so we have to use this one instead.
{% endcomment %}
{{ block.super }}
{% custom_js %}
{% endblock footer %}

View File

@ -1,94 +0,0 @@
{% extends 'admin/change_form.html' %}
{% block object-tools-items %}
<li>
<a href="{{download_url}}">
Download file
</a>
</li>
{{ block.super }}
{% endblock %}
{% block content %}
{{ block.super }}
<div class="side-preview">
<h2>Preview</h2>
<object data="/fetch/preview/{{object_id}}"></object>
</div>
{% if file_type in "pdf jpg png" %}
<div id="change_form_twocolumn_parent">
<div id="change_form_form_parent"></div>
<div id="change_form_viewer_parent">
{% if file_type == "pdf" %}
{% include "admin/documents/document/viewers/viewer_pdf.html" %}
{% endif %}
{% if file_type in "jpg png" %}
{% include "admin/documents/document/viewers/viewer_image.html" %}
{% endif %}
</div>
</div>
<script>
django.jQuery("#change_form_form_parent").append(django.jQuery("#document_form"));
django.jQuery("#content-main").append(django.jQuery("#change_form_twocolumn_parent"));
</script>
{% endif %}
{% if next_object %}
<script type="text/javascript">//<![CDATA[
(function($){
$('<input type="submit" value="Save and edit next" name="_saveandeditnext" />')
.prependTo('div.submit-row');
$('<input type="hidden" value="{{next_object}}" name="_next_object" />')
.prependTo('div.submit-row');
})(django.jQuery);
//]]></script>
{% endif %}
{% endblock content %}
{% block extrastyle %}
{{ block.super }}
<style>
.side-preview {
width: 100%;
height: 800px;
clear: both;
}
.side-preview object {
height: 100%;
width: 100%;
}
@media screen and (min-width: 1500px) {
#content-main {
width: 50%;
}
#footer {
padding: 0;
}
.side-preview {
float: right;
width: 40%;
height: 80vh;
clear: none;
}
}
</style>
{% endblock %}
{% block footer %}
{{ block.super }}
{# Hack to force Django to make the created date a date input rather than `text` (the default) #}
<script>
django.jQuery(".field-created input").first().attr("type", "date")
</script>
{% endblock footer %}

View File

@ -1,12 +0,0 @@
{% extends 'admin/change_list.html' %}
{% load admin_actions from admin_list%}
{% load result_list from hacks %}
{% block result_list %}
{% if action_form and actions_on_top and cl.show_admin_actions %}{% admin_actions %}{% endif %}
{% result_list cl %}
{% if action_form and actions_on_bottom and cl.show_admin_actions %}{% admin_actions %}{% endif %}
{% endblock %}

View File

@ -1,228 +0,0 @@
{% load i18n %}
<style>
.grid *, .grid *:after, .grid *:before {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.grid {
display: flex;
flex-wrap: wrap;
margin: 10px 0px 10px 10px;
}
.box {
width: 12.5%;
padding-right: 10px;
padding-bottom: 15px;
opacity: 0.8;
transition: all 0.5s;
}
.box:hover {
opacity: 1;
transition: all 0.5s;
}
.result {
border: 1px solid #cccccc;
border-radius: 2%;
overflow: hidden;
height: 350px;
position: relative;
box-shadow: 1px 1px 4px #cccccc;
}
.result .header {
padding: 5px;
background-color: #90a9b7;
position: relative;
}
.result .header .checkbox {
width: 5%;
float: left;
position: absolute;
z-index: 2;
}
.result .header .info {
margin-left: 10%;
position: relative;
}
.headerLink {
cursor: pointer;
opacity: 0;
z-index: 1;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.header > a {
z-index: 2;
margin-left: 10%;
position: relative;
}
.result .header a,
.result a.tag {
color: #ffffff;
}
.result .documentType {
padding: 5px;
background-color: #eeeeee;
text-align: center;
}
.result .date {
padding: 5px;
}
.result .tags {
float: left;
}
.result .tags a.tag {
padding: 2px 5px;
border-radius: 2px;
display: inline-block;
margin: 2px;
}
.result .date {
float: right;
color: #cccccc;
}
.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:after {
content: "";
display: table;
clear: both;
}
@media (max-width: 1600px) {
.box {
width: 25%
}
}
@media (max-width: 991px) {
.grid {
margin-right: 220px;
}
.box {
width: 50%
}
}
@media (max-width: 767px) {
.grid {
margin-right: 0;
}
}
@media (max-width: 500px) {
.box {
width: 100%
}
}
</style>
{# This is just copypasta from the parent change_list_results.html file #}
{% if result_hidden_fields %}
<div class="hiddenfields">{# DIV for HTML validation #}
{% for item in result_hidden_fields %}{{ item }}{% endfor %}
</div>
{% endif %}
{% if results %}
<div class="results">
<table id="result_list">
<thead>
<tr>
{% for header in result_headers %}
<th scope="col" {{ header.class_attrib }}>
{% if header.sortable %}
{% if header.sort_priority > 0 %}
<div class="sortoptions">
<a class="sortremove" href="{{ header.url_remove }}" title="{% trans "Remove from sorting" %}"></a>
{% if num_sorted_fields > 1 %}<span class="sortpriority" title="{% blocktrans with priority_number=header.sort_priority %}Sorting priority: {{ priority_number }}{% endblocktrans %}">{{ header.sort_priority }}</span>{% endif %}
<a href="{{ header.url_toggle }}" class="toggle {% if header.ascending %}ascending{% else %}descending{% endif %}" title="{% trans "Toggle sorting" %}"></a>
</div>
{% endif %}
{% endif %}
<div class="text">{% if header.sortable %}<a href="{{ header.url_primary }}">{{ header.text|capfirst }}</a>{% else %}<span>{{ header.text|capfirst }}</span>{% endif %}</div>
<div class="clear"></div>
</th>{% endfor %}
</tr>
</thead>
</table>
{% endif %}
{# /copypasta #}
<div class="grid">
{% for result in results %}
{# 0: Checkbox #}
{# 1: Title #}
{# 2: Date #}
{# 3: Added #}
{# 4: Image #}
{# 5: Correspondent #}
{# 6: Tags #}
{# 7: Archive serial number #}
{# 8: Document type #}
{# 9: Document edit url #}
<div class="box">
<div class="result">
<div class="header">
{% comment %}
The purpose of 'headerLink' is to make the whole header
background clickable.
We use an onclick handler here instead of a native link ('<a>')
to allow selecting (and copying) the overlying doc title text
with the mouse cursor.
If the title link were layered upon another link ('<a>'), title text
selection would not be possible with mouse click + drag. Instead,
the underlying link would be dragged.
{% endcomment %}
<div class="headerLink" onclick="location.href='{{ result.9 }}';"></div>
<div class="checkbox">{{ result.0 }}</div>
<div class="info">
{{ result.5 }}
</div>
{{ result.1 }}
<div style="clear: both;"></div>
</div>
{% if '>-<' not in result.8 %}<div class="documentType">{{ result.8 }}</div>{% endif %}
<div class="tags">{{ result.6 }}</div>
<div class="date">{{ result.2 }}</div>
<div style="clear: both;"></div>
<div class="image">{{ result.4 }}</div>
{# 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 %}<div class="footer">#{{ result.7 }}</div>{% endif %}
</div>
</div>
{% endfor %}
</div>
<script>
// We need to re-build the select-all functionality as the old logic pointed
// to a table and we're using divs now.
django.jQuery("#action-toggle").on("change", function(){
django.jQuery(".grid .box .result .checkbox input")
.prop("checked", this.checked);
});
</script>
</div>

View File

@ -1,50 +0,0 @@
{% extends "admin/base_site.html" %}
{% load i18n l10n admin_urls static %}
{% load staticfiles %}
{% block extrahead %}
{{ block.super }}
{{ media }}
<script type="text/javascript" src="{% static 'admin/js/cancel.js' %}"></script>
{% endblock %}
{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation delete-selected-confirmation{% endblock %}
{% block breadcrumbs %}
<div class="breadcrumbs">
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
&rsaquo; {{ title }}
</div>
{% endblock %}
{% block content %}
<p>Please select the {{itemname}}.</p>
<form method="post">{% csrf_token %}
<div>
{% for obj in queryset %}
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}"/>
{% endfor %}
<p>
<select name="obj_id">
{% for obj in objects %}
<option value="{{ obj.id }}">{{ obj.name }}</option>
{% endfor %}
</select>
</p>
<input type="hidden" name="action" value="{{ action }}"/>
<input type="hidden" name="post" value="yes" />
<p>
<input type="submit" value="{% trans 'Confirm' %}" />
<a href="#" class="button cancel-link">{% trans "Go back" %}</a>
</p>
</div>
</form>
{% endblock %}

View File

@ -1 +0,0 @@
<img src="{{download_url}}" style="max-width: 100%">

View File

@ -1,130 +0,0 @@
{% load static %}
<div>
<input id="prev" value="Previous" class="default" type="button">
<input id="next" value="Next" class="default" type="button">
&nbsp; &nbsp;
<span>Page: <span id="page_num"></span> / <span id="page_count"></span></span>
&nbsp; &nbsp;
<input id="zoomin" value="+" class="default" type="button">
<input id="zoomout" value="-" class="default" type="button">
</div>
<div style="width: 100%; overflow: auto;">
<canvas id="the-canvas"></canvas>
</div>
<script type="text/javascript" src="{% static 'documents/js/pdf.js' %}"></script>
<script type="text/javascript" src="{% static 'documents/js/pdf.worker.js' %}"></script>
{# Load and display PDF document#}
<script>
var pdfjsLib = window['pdfjs-dist/build/pdf'];
var pdfDoc = null,
pageNum = 1,
pageRendering = false,
pageNumPending = null,
scale = 1.44,
canvas = document.getElementById('the-canvas'),
ctx = canvas.getContext('2d');
/**
* Get page info from document, resize canvas accordingly, and render page.
* @param num Page number.
*/
function renderPage(num) {
pageRendering = true;
// Using promise to fetch the page
pdfDoc.getPage(num).then(function(page) {
var viewport = page.getViewport(scale);
canvas.height = viewport.height;
canvas.width = viewport.width;
// Render PDF page into canvas context
var renderContext = {
canvasContext: ctx,
viewport: viewport
};
var renderTask = page.render(renderContext);
// Wait for rendering to finish
renderTask.promise.then(function () {
pageRendering = false;
if (pageNumPending !== null) {
// New page rendering is pending
renderPage(pageNumPending);
pageNumPending = null;
}
});
});
// Update page counters
document.getElementById('page_num').textContent = num;
}
/**
* If another page rendering in progress, waits until the rendering is
* finised. Otherwise, executes rendering immediately.
*/
function queueRenderPage(num) {
if (pageRendering) {
pageNumPending = num;
} else {
renderPage(num);
}
}
/**
* Displays previous page.
*/
function onPrevPage() {
if (pageNum <= 1) {
return;
}
pageNum--;
queueRenderPage(pageNum);
}
document.getElementById('prev').addEventListener('click', onPrevPage);
/**
* Displays next page.
*/
function onNextPage() {
if (pageNum >= pdfDoc.numPages) {
return;
}
pageNum++;
queueRenderPage(pageNum);
}
document.getElementById('next').addEventListener('click', onNextPage);
/**
* Displays next page.
*/
function onZoomIn() {
scale *= 1.2;
queueRenderPage(pageNum);
}
document.getElementById('zoomin').addEventListener('click', onZoomIn);
/**
* Displays next page.
*/
function onZoomOut() {
scale /= 1.2;
queueRenderPage(pageNum);
}
document.getElementById('zoomout').addEventListener('click', onZoomOut);
/**
* Asynchronously downloads PDF.
*/
pdfjsLib.getDocument("{{download_url}}").then(function (pdfDoc_) {
pdfDoc = pdfDoc_;
document.getElementById('page_count').textContent = pdfDoc.numPages;
// Initial/first page rendering
renderPage(pageNum);
});
</script>

View File

@ -1,57 +0,0 @@
{% extends "admin/index.html" %}
{% load i18n static %}
{# This block adds a search form on the admin start page and on the module start page so that #}
{# the user can quickly search for documents #}
{% block pretitle %}
<div>
<h3>{% trans 'Search documents' %}</h3>
<div id="toolbar"><form id="changelist-search" method="get" action="{% url 'admin:documents_document_changelist' %}">
<div><!-- DIV needed for valid HTML -->
<label for="searchbar"><img src="{% static "admin/img/search.svg" %}" alt="Search"></label>
<input type="text" size="40" name="q" value="" id="searchbar" autofocus="">
<input type="submit" value="{% trans 'Search' %}">
</div>
</form>
</div>
</div>
{% endblock %}
{# This whole block is here just to override the `get_admin_log` line so #}
{# that the log entries aren't limited to the current user #}
{% block sidebar %}
<div id="content-related">
<div class="module" id="recent-actions-module">
<h2>{% trans 'Recent actions' %}</h2>
<h3>{% trans 'My actions' %}</h3>
{% load log %}
{% get_admin_log 10 as admin_log %}
{% if not admin_log %}
<p>{% trans 'None available' %}</p>
{% else %}
<ul class="actionlist">
{% for entry in admin_log %}
<li class="{% if entry.is_addition %}addlink{% endif %}{% if entry.is_change %}changelink{% endif %}{% if entry.is_deletion %}deletelink{% endif %}">
{% if entry.is_deletion or not entry.get_admin_url %}
{{ entry.object_repr }}
{% else %}
<a href="{{ entry.get_admin_url }}">{{ entry.object_repr }}</a>
{% endif %}
<br/>
{% if entry.content_type %}
<span class="mini quiet">{% filter capfirst %}{{ entry.content_type }}{% endfilter %}</span>
{% else %}
<span class="mini quiet">{% trans 'Unknown content' %}</span>
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
</div>
</div>
{% endblock %}

View File

@ -1,37 +0,0 @@
import os
from django import template
from django.conf import settings
from django.utils.safestring import mark_safe
register = template.Library()
@register.simple_tag()
def custom_css():
theme_path = os.path.join(
settings.MEDIA_ROOT,
"overrides.css"
)
if os.path.exists(theme_path):
return mark_safe(
'<link rel="stylesheet" type="text/css" href="{}" />'.format(
os.path.join(settings.MEDIA_URL, "overrides.css")
)
)
return ""
@register.simple_tag()
def custom_js():
theme_path = os.path.join(
settings.MEDIA_ROOT,
"overrides.js"
)
if os.path.exists(theme_path):
return mark_safe(
'<script src="{}"></script>'.format(
os.path.join(settings.MEDIA_URL, "overrides.js")
)
)
return ""

View File

@ -1,43 +0,0 @@
import re
from django.contrib.admin.templatetags.admin_list import (
result_headers,
result_hidden_fields,
results
)
from django.template import Library
EXTRACT_URL = re.compile(r'href="(.*?)"')
register = Library()
@register.inclusion_tag("admin/documents/document/change_list_results.html")
def result_list(cl):
"""
Copy/pasted from django.contrib.admin.templatetags.admin_list just so I can
modify the value passed to `.inclusion_tag()` in the decorator here. There
must be a cleaner way... right?
"""
headers = list(result_headers(cl))
num_sorted_fields = 0
for h in headers:
if h['sortable'] and h['sorted']:
num_sorted_fields += 1
return {'cl': cl,
'result_hidden_fields': list(result_hidden_fields(cl)),
'result_headers': headers,
'num_sorted_fields': num_sorted_fields,
'results': map(add_doc_edit_url, results(cl))}
def add_doc_edit_url(result):
"""
Make the document edit URL accessible to the view as a separate item
"""
title = result[1]
match = re.search(EXTRACT_URL, title)
edit_doc_url = match.group(1)
result.append(edit_doc_url)
return result

View File

@ -18,7 +18,9 @@ from dotenv import load_dotenv
# Tap paperless.conf if it's available
if os.path.exists("/etc/paperless.conf"):
if os.path.exists("paperless.conf"):
load_dotenv("paperless.conf")
elif os.path.exists("/etc/paperless.conf"):
load_dotenv("/etc/paperless.conf")
elif os.path.exists("/usr/local/etc/paperless.conf"):
load_dotenv("/usr/local/etc/paperless.conf")
@ -109,7 +111,7 @@ MIDDLEWARE = [
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
# We allow CORS from localhost:8080
CORS_ORIGIN_WHITELIST = tuple(os.getenv("PAPERLESS_CORS_ALLOWED_HOSTS", "http://localhost:8080,https://localhost:8080").split(","))
CORS_ORIGIN_WHITELIST = tuple(os.getenv("PAPERLESS_CORS_ALLOWED_HOSTS", "http://localhost:8080,https://localhost:8080,localhost:4200").split(","))
# If auth is disabled, we just use our "bypass" authentication middleware
if bool(os.getenv("PAPERLESS_DISABLE_LOGIN", "false").lower() in ("yes", "y", "1", "t", "true")):