diff --git a/docs/changelog.rst b/docs/changelog.rst index 5a535b33b..9cb1f2784 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,15 @@ Changelog ######### +* 0.3.5 + * A serious facelift for the documents listing page wherein we drop the + tabular layout in favour of a tiled interface. + * Users can now configure the number of items per page. + * Fix for `#171`_: Allow users to specify their own ``SECRET_KEY`` value. + * Moved the dotenv loading to the top of settings.py + * Fix for `#112`_: Added checks for binaries required for document + consumption. + * 0.3.4 * Removal of django-suit due to a licensing conflict I bumped into in 0.3.3. Note that you *can* use Django Suit with Paperless, but only in a @@ -42,7 +51,8 @@ Changelog ``paperless.conf``. * `#148`_: The database location (sqlite) is now a variable you can set in ``paperless.conf``. - * `#146`_: Fixed a bug that allowed unauthorised access to the `/fetch` URL. + * `#146`_: Fixed a bug that allowed unauthorised access to the ``/fetch`` + URL. * `#131`_: Document files are now automatically removed from disk when they're deleted in Paperless. * `#121`_: Fixed a bug where Paperless wasn't setting document creation time @@ -168,10 +178,12 @@ Changelog .. _#89: https://github.com/danielquinn/paperless/issues/89 .. _#94: https://github.com/danielquinn/paperless/issues/94 .. _#98: https://github.com/danielquinn/paperless/issues/98 +.. _#112: https://github.com/danielquinn/paperless/issues/112 .. _#121: https://github.com/danielquinn/paperless/issues/121 .. _#131: https://github.com/danielquinn/paperless/issues/131 .. _#146: https://github.com/danielquinn/paperless/issues/146 .. _#148: https://github.com/danielquinn/paperless/pull/148 .. _#150: https://github.com/danielquinn/paperless/pull/150 +.. _#171: https://github.com/danielquinn/paperless/issues/171 .. _#172: https://github.com/danielquinn/paperless/issues/172 .. _#179: https://github.com/danielquinn/paperless/pull/179 diff --git a/requirements.txt b/requirements.txt index f81cc86fd..23d8f0b63 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,18 +1,18 @@ -Django==1.10.4 +Django==1.10.5 Pillow>=3.1.1 -django-crispy-forms>=1.6.0 -django-extensions>=1.6.1 +django-crispy-forms>=1.6.1 +django-extensions>=1.7.6 django-filter>=1.0 -djangorestframework>=3.4.4 +django-flat-responsive>=1.2.0 +djangorestframework>=3.5.3 filemagic>=1.6 -langdetect>=1.0.5 -pyocr>=0.3.1 -python-dateutil>=2.4.2 -python-dotenv>=0.3.0 -python-gnupg>=0.3.8 -pytz>=2015.7 +langdetect>=1.0.7 +pyocr>=0.4.6 +python-dateutil>=2.6.0 +python-dotenv>=0.6.2 +python-gnupg>=0.3.9 +pytz>=2016.10 gunicorn==19.6.0 -django-flat-responsive==1.2.0 # For the tests pytest diff --git a/src/documents/admin.py b/src/documents/admin.py index 0601f579e..3022e3c61 100644 --- a/src/documents/admin.py +++ b/src/documents/admin.py @@ -58,7 +58,7 @@ class DocumentAdmin(CommonAdmin): } search_fields = ("correspondent__name", "title", "content") - list_display = ("created", "title", "thumbnail", "correspondent", "tags_") + list_display = ("title", "created", "thumbnail", "correspondent", "tags_") list_filter = ("tags", "correspondent", MonthListFilter) ordering = ["-created", "correspondent"] diff --git a/src/documents/templates/admin/change_list_results.html b/src/documents/templates/admin/change_list_results.html new file mode 100644 index 000000000..35288ccd0 --- /dev/null +++ b/src/documents/templates/admin/change_list_results.html @@ -0,0 +1,6 @@ +{% load hacks %} + +{# See documents.templatetags.hacks.change_list_results for an explanation #} + +{% change_list_results %} + diff --git a/src/documents/templates/admin/documents/document/change_list_results.html b/src/documents/templates/admin/documents/document/change_list_results.html new file mode 100644 index 000000000..0730f92b3 --- /dev/null +++ b/src/documents/templates/admin/documents/document/change_list_results.html @@ -0,0 +1,167 @@ +{% load i18n %} + +<style> + .grid *, .grid *:after, .grid *:before { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + .box { + width: 12.5%; + padding: 1em; + float: left; + opacity: 0.7; + transition: all 0.5s; + } + .box:hover { + opacity: 1; + transition: all 0.5s; + } + .box:last-of-type { + padding-right: 0; + } + .result { + border: 1px solid #cccccc; + border-radius: 2%; + overflow: hidden; + height: 300px; + } + .result .header { + padding: 5px; + background-color: #79AEC8; + height: 6em; + } + .result .header .checkbox { + margin-right: 5px; + } + .result .header .checkbox{ + width: 5%; + float: left; + } + .result .header .info { + width: 90%; + float: left; + } + .result .header a, + .result a.tag { + color: #ffffff; + } + .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%; + } + + .grid { + margin-right: 260px; + } + .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 #} +<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> +{# /copypasta #} + + +<div class="grid"> + {% for result in results %} + {# 0: Checkbox #} + {# 1: Title #} + {# 2: Date #} + {# 3: Image #} + {# 4: Correspondent #} + {# 5: Tags #} + <div class="box"> + <div class="result"> + <div class="header"> + <div class="checkbox">{{ result.0 }}</div> + <div class="info"> + {{ result.4 }}<br /> + {{ result.1 }} + </div> + <div style="clear: both;"></div> + </div> + <div class="tags">{{ result.5 }}</div> + <div class="date">{{ result.2 }}</div> + <div style="clear: both;"></div> + <div class="image">{{ result.3 }}</div> + </div> + </div> + {% endfor %} +</div> + + +<script> + // We nee 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> diff --git a/src/documents/templatetags/__init__.py b/src/documents/templatetags/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/documents/templatetags/hacks.py b/src/documents/templatetags/hacks.py new file mode 100644 index 000000000..c8229cbb9 --- /dev/null +++ b/src/documents/templatetags/hacks.py @@ -0,0 +1,41 @@ +import os + +from django.contrib import admin +from django.template import Library +from django.template.loader import get_template + +from ..models import Document + + +register = Library() + + +@register.simple_tag(takes_context=True) +def change_list_results(context): + """ + Django has a lot of places where you can override defaults, but + unfortunately, `change_list_results.html` is not one of them. In fact, + it's a downright pain in the ass to override this file on a per-model basis + and this is the cleanest way I could come up with. + + Basically all we've done here is defined `change_list_results.html` in an + `admin` directory which globally overrides that file for *every* model. + That template however simply loads this templatetag which determines + whether we're currently looking at a `Document` listing or something else + and loads the appropriate file in each case. + + Better work arounds for this are welcome as I hate this myself, but at the + moment, it's all I could come up with. + """ + + path = os.path.join( + os.path.dirname(admin.__file__), + "templates", + "admin", + "change_list_results.html" + ) + + if context["cl"].model == Document: + path = "admin/documents/document/change_list_results.html" + + return get_template(path).render(context) diff --git a/src/paperless/settings.py b/src/paperless/settings.py index e8afb9545..8f03942dd 100644 --- a/src/paperless/settings.py +++ b/src/paperless/settings.py @@ -52,18 +52,19 @@ if _allowed_hosts: INSTALLED_APPS = [ - 'flat_responsive', - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", "django_extensions", "documents.apps.DocumentsConfig", + "flat_responsive", + "django.contrib.admin", + "rest_framework", "crispy_forms", diff --git a/src/paperless/version.py b/src/paperless/version.py index 2af29977a..9e75602fc 100644 --- a/src/paperless/version.py +++ b/src/paperless/version.py @@ -1 +1 @@ -__version__ = (0, 3, 4) +__version__ = (0, 3, 5)