Convert document thumbnails, preview, image preview and staging files to template base widgets. Unify all updated widgets. Display resolution settings are now specified as width and height and not a single resolution value.
Signed-off-by: Michael Price <loneviking72@gmail.com>
This commit is contained in:
committed by
Roberto Rosario
parent
d29d4ba110
commit
8590bff6e4
@@ -33,7 +33,12 @@ Next (2018-XX-XX)
|
||||
- Documents without at least a version are not scanned for duplicates.
|
||||
- Use a SHA256 hex digest of the secret key at the name of the lockfile. This makes the generation of the name repeatable while unique between installations.
|
||||
- Squashed apps migrations.
|
||||
|
||||
- Convert document thumbnails, preview, image preview and staging files to template base widgets.
|
||||
- Unify all document widgets.
|
||||
- Display resolution settings are now specified as width and height and not a single resolution value.
|
||||
- Printed pages are now full width.
|
||||
- Move the invalid document markup to a separate HTML template.
|
||||
|
||||
2.8 (2018-02-27)
|
||||
================
|
||||
- Rename the role groups link label from "Members" to "Groups".
|
||||
|
||||
@@ -27,4 +27,3 @@ class Icon(object):
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -120,6 +120,7 @@ img.lazy-load-carousel {
|
||||
|
||||
.instance-image-widget {
|
||||
text-align: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
hr {
|
||||
@@ -700,3 +701,9 @@ a i {
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 6px solid #be2626; }
|
||||
|
||||
|
||||
.staging-file-thumbnail-container {
|
||||
width: 150px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
@@ -98,13 +98,6 @@ def get_descriptor(file_input, read=True):
|
||||
return file_input
|
||||
|
||||
|
||||
def index_or_default(instance, index, default):
|
||||
try:
|
||||
return instance[index]
|
||||
except IndexError:
|
||||
return default
|
||||
|
||||
|
||||
def TemporaryFile(*args, **kwargs):
|
||||
kwargs.update({'dir': setting_temporary_directory.value})
|
||||
return tempfile.TemporaryFile(*args, **kwargs)
|
||||
|
||||
@@ -10,5 +10,3 @@ DEFAULT_PDFTOPPM_DPI = 300
|
||||
DEFAULT_PDFTOPPM_FORMAT = 'jpeg' # Possible values jpeg, png, tiff
|
||||
DEFAULT_PDFTOPPM_PATH = '/usr/bin/pdftoppm'
|
||||
DEFAULT_PDFINFO_PATH = '/usr/bin/pdfinfo'
|
||||
|
||||
DIMENSION_SEPARATOR = 'x'
|
||||
|
||||
@@ -337,8 +337,12 @@ class APIDocumentPageImageView(generics.RetrieveAPIView):
|
||||
GET:
|
||||
omit_serializer: true
|
||||
parameters:
|
||||
- name: size
|
||||
description: 'x' seprated width and height of the desired image representation.
|
||||
- name: width
|
||||
description: Width of the desired image representation.
|
||||
paramType: query
|
||||
type: number
|
||||
- name: height
|
||||
description: Height of the desired image representation.
|
||||
paramType: query
|
||||
type: number
|
||||
- name: zoom
|
||||
@@ -377,7 +381,8 @@ class APIDocumentPageImageView(generics.RetrieveAPIView):
|
||||
return None
|
||||
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
size = request.GET.get('size')
|
||||
width = request.GET.get('width')
|
||||
height = request.GET.get('height')
|
||||
zoom = request.GET.get('zoom')
|
||||
|
||||
if zoom:
|
||||
@@ -390,8 +395,8 @@ class APIDocumentPageImageView(generics.RetrieveAPIView):
|
||||
|
||||
task = task_generate_document_page_image.apply_async(
|
||||
kwargs=dict(
|
||||
document_page_id=self.get_object().pk, size=size, zoom=zoom,
|
||||
rotation=rotation
|
||||
document_page_id=self.get_object().pk, width=width,
|
||||
height=height, zoom=zoom, rotation=rotation
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -101,8 +101,7 @@ from .statistics import (
|
||||
total_document_page_per_month, total_document_version_per_month
|
||||
)
|
||||
from .widgets import (
|
||||
DocumentThumbnailWidget, DocumentPageThumbnailWidget,
|
||||
DocumentVersionThumbnailWidget, widget_document_page_number,
|
||||
DocumentPageThumbnailWidget, widget_document_page_number,
|
||||
widget_document_version_page_number
|
||||
)
|
||||
|
||||
@@ -207,13 +206,11 @@ class DocumentsApp(MayanAppConfig):
|
||||
|
||||
# Document and document page thumbnail widget
|
||||
document_page_thumbnail_widget = DocumentPageThumbnailWidget()
|
||||
document_thumbnail_widget = DocumentThumbnailWidget()
|
||||
document_version_thumbnail_widget = DocumentVersionThumbnailWidget()
|
||||
|
||||
# Document
|
||||
SourceColumn(
|
||||
source=Document, label=_('Thumbnail'),
|
||||
func=lambda context: document_thumbnail_widget.render(
|
||||
func=lambda context: document_page_thumbnail_widget.render(
|
||||
instance=context['object']
|
||||
)
|
||||
)
|
||||
@@ -263,7 +260,7 @@ class DocumentsApp(MayanAppConfig):
|
||||
# DeletedDocument
|
||||
SourceColumn(
|
||||
source=DeletedDocument, label=_('Thumbnail'),
|
||||
func=lambda context: document_thumbnail_widget.render(
|
||||
func=lambda context: document_page_thumbnail_widget.render(
|
||||
instance=context['object']
|
||||
)
|
||||
)
|
||||
@@ -279,7 +276,7 @@ class DocumentsApp(MayanAppConfig):
|
||||
# DocumentVersion
|
||||
SourceColumn(
|
||||
source=DocumentVersion, label=_('Thumbnail'),
|
||||
func=lambda context: document_version_thumbnail_widget.render(
|
||||
func=lambda context: document_page_thumbnail_widget.render(
|
||||
instance=context['object']
|
||||
)
|
||||
)
|
||||
@@ -309,7 +306,7 @@ class DocumentsApp(MayanAppConfig):
|
||||
# DuplicatedDocument
|
||||
SourceColumn(
|
||||
source=DuplicatedDocument, label=_('Thumbnail'),
|
||||
func=lambda context: document_thumbnail_widget.render(
|
||||
func=lambda context: document_page_thumbnail_widget.render(
|
||||
instance=context['object'].document
|
||||
)
|
||||
)
|
||||
|
||||
17
mayan/apps/documents/fields.py
Normal file
17
mayan/apps/documents/fields.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django import forms
|
||||
|
||||
from .widgets import DocumentPagesCarouselWidget, DocumentPageImageWidget
|
||||
|
||||
|
||||
class DocumentField(forms.fields.Field):
|
||||
widget = DocumentPagesCarouselWidget
|
||||
|
||||
|
||||
class DocumentPageField(forms.fields.Field):
|
||||
widget = DocumentPageImageWidget
|
||||
|
||||
|
||||
class DocumentVersionField(forms.fields.Field):
|
||||
widget = DocumentPagesCarouselWidget
|
||||
@@ -9,35 +9,34 @@ from django.utils.translation import ugettext_lazy as _
|
||||
from acls.models import AccessControlList
|
||||
from common.forms import DetailForm
|
||||
|
||||
from .fields import (
|
||||
DocumentField, DocumentPageField, DocumentVersionField
|
||||
)
|
||||
from .models import (
|
||||
Document, DocumentType, DocumentPage, DocumentTypeFilename
|
||||
Document, DocumentType, DocumentTypeFilename
|
||||
)
|
||||
from .literals import DEFAULT_ZIP_FILENAME, PAGE_RANGE_ALL, PAGE_RANGE_CHOICES
|
||||
from .permissions import permission_document_create
|
||||
from .settings import setting_language_choices
|
||||
from .widgets import DocumentPagesCarouselWidget, DocumentPageImageWidget
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Document page forms
|
||||
class DocumentPageForm(DetailForm):
|
||||
class Meta:
|
||||
fields = ()
|
||||
model = DocumentPage
|
||||
|
||||
|
||||
class DocumentPageForm(forms.Form):
|
||||
def __init__(self, *args, **kwargs):
|
||||
zoom = kwargs.pop('zoom', None)
|
||||
instance = kwargs.pop('instance', None)
|
||||
rotation = kwargs.pop('rotation', None)
|
||||
zoom = kwargs.pop('zoom', None)
|
||||
super(DocumentPageForm, self).__init__(*args, **kwargs)
|
||||
self.fields['page_image'].initial = self.instance
|
||||
self.fields['page_image'].widget.attrs.update({
|
||||
self.fields['document_page'].initial = instance
|
||||
self.fields['document_page'].widget.attrs.update({
|
||||
'zoom': zoom,
|
||||
'rotation': rotation
|
||||
'rotation': rotation,
|
||||
})
|
||||
|
||||
page_image = forms.CharField(
|
||||
label=_('Page image'), widget=DocumentPageImageWidget()
|
||||
)
|
||||
document_page = DocumentPageField()
|
||||
|
||||
|
||||
# Document forms
|
||||
@@ -45,31 +44,18 @@ class DocumentPreviewForm(forms.Form):
|
||||
def __init__(self, *args, **kwargs):
|
||||
document = kwargs.pop('instance', None)
|
||||
super(DocumentPreviewForm, self).__init__(*args, **kwargs)
|
||||
self.fields['preview'].initial = document
|
||||
try:
|
||||
self.fields['preview'].label = _(
|
||||
'Document pages (%d)'
|
||||
) % document.page_count
|
||||
except AttributeError:
|
||||
self.fields['preview'].label = _('Document pages (%d)') % 0
|
||||
self.fields['document'].initial = document
|
||||
|
||||
preview = forms.CharField(widget=DocumentPagesCarouselWidget())
|
||||
document = DocumentField()
|
||||
|
||||
|
||||
class DocumentVersionPreviewForm(forms.Form):
|
||||
def __init__(self, *args, **kwargs):
|
||||
document_version = kwargs.pop('instance', None)
|
||||
super(DocumentVersionPreviewForm, self).__init__(*args, **kwargs)
|
||||
self.fields['document_version'].initial = document_version
|
||||
|
||||
self.fields['preview'].initial = document_version
|
||||
try:
|
||||
self.fields['preview'].label = _(
|
||||
'Document pages (%d)'
|
||||
) % document_version.pages.count()
|
||||
except AttributeError:
|
||||
self.fields['preview'].label = _('Document version pages (%d)') % 0
|
||||
|
||||
preview = forms.CharField(widget=DocumentPagesCarouselWidget())
|
||||
document_version = DocumentVersionField()
|
||||
|
||||
|
||||
class DocumentForm(forms.ModelForm):
|
||||
|
||||
@@ -39,8 +39,8 @@ from .permissions import permission_document_view
|
||||
from .runtime import cache_storage_backend, storage_backend
|
||||
from .settings import (
|
||||
setting_disable_base_image_cache, setting_disable_transformed_image_cache,
|
||||
setting_display_size, setting_language, setting_zoom_max_level,
|
||||
setting_zoom_min_level
|
||||
setting_display_width, setting_display_height, setting_language,
|
||||
setting_zoom_max_level, setting_zoom_min_level
|
||||
)
|
||||
from .signals import (
|
||||
post_document_created, post_document_type_change, post_version_upload
|
||||
@@ -195,6 +195,11 @@ class Document(models.Model):
|
||||
passthrough = PassthroughManager()
|
||||
trash = TrashCanManager()
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Document')
|
||||
verbose_name_plural = _('Documents')
|
||||
ordering = ('-date_added',)
|
||||
|
||||
def __str__(self):
|
||||
return self.label or ugettext('Document stub, id: %d') % self.pk
|
||||
|
||||
@@ -238,11 +243,6 @@ class Document(models.Model):
|
||||
if _commit_events:
|
||||
event_document_properties_edit.commit(actor=user, target=self)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Document')
|
||||
verbose_name_plural = _('Documents')
|
||||
ordering = ('-date_added',)
|
||||
|
||||
def add_as_recent_document_for_user(self, user):
|
||||
return RecentDocument.objects.add_document_for_user(user, self)
|
||||
|
||||
@@ -257,6 +257,11 @@ class Document(models.Model):
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_api_image_url(self):
|
||||
latest_version = self.latest_version
|
||||
if latest_version:
|
||||
return latest_version.get_api_image_url()
|
||||
|
||||
def invalidate_cache(self):
|
||||
for document_version in self.versions.all():
|
||||
document_version.invalidate_cache()
|
||||
@@ -421,6 +426,11 @@ class DocumentVersion(models.Model):
|
||||
def get_absolute_url(self):
|
||||
return reverse('documents:document_version_view', args=(self.pk,))
|
||||
|
||||
def get_api_image_url(self):
|
||||
first_page = self.pages.first()
|
||||
if first_page:
|
||||
return first_page.get_api_image_url()
|
||||
|
||||
def get_rendered_string(self, preserve_extension=False):
|
||||
if preserve_extension:
|
||||
filename, extension = os.path.splitext(self.document.label)
|
||||
@@ -758,7 +768,8 @@ class DocumentPage(models.Model):
|
||||
|
||||
# Set sensible defaults if the argument is not specified or if the
|
||||
# argument is None
|
||||
size = kwargs.get('size', setting_display_size.value) or setting_display_size.value
|
||||
width = kwargs.get('width', setting_display_width.value) or setting_display_width.value
|
||||
height = kwargs.get('height', setting_display_height.value) or setting_display_height.value
|
||||
rotation = kwargs.get('rotation', DEFAULT_ROTATION) or DEFAULT_ROTATION
|
||||
zoom_level = kwargs.get('zoom', DEFAULT_ZOOM_LEVEL) or DEFAULT_ZOOM_LEVEL
|
||||
|
||||
@@ -785,11 +796,9 @@ class DocumentPage(models.Model):
|
||||
TransformationRotate(degrees=rotation)
|
||||
)
|
||||
|
||||
if size:
|
||||
if width:
|
||||
transformation_list.append(
|
||||
TransformationResize(
|
||||
**dict(zip(('width', 'height'), (size.split('x'))))
|
||||
)
|
||||
TransformationResize(width=width, height=height)
|
||||
)
|
||||
|
||||
if zoom_level:
|
||||
@@ -875,6 +884,13 @@ class DocumentPage(models.Model):
|
||||
"""
|
||||
return '{}-{}'.format(self.document_version.uuid, self.pk)
|
||||
|
||||
def get_api_image_url(self):
|
||||
return reverse(
|
||||
'rest_api:documentpage-image', args=(
|
||||
self.document.pk, self.document_version.pk, self.pk
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class DocumentPageCachedImage(models.Model):
|
||||
document_page = models.ForeignKey(
|
||||
|
||||
@@ -10,18 +10,31 @@ LANGUAGE_CHOICES = [
|
||||
(i.iso639_3_code, i.name) for i in list(pycountry.languages)
|
||||
]
|
||||
|
||||
|
||||
namespace = Namespace(name='documents', label=_('Documents'))
|
||||
setting_display_size = namespace.add_setting(
|
||||
global_name='DOCUMENTS_DISPLAY_SIZE', default='3600'
|
||||
setting_display_width = namespace.add_setting(
|
||||
global_name='DOCUMENTS_DISPLAY_WIDTH', default='3600'
|
||||
)
|
||||
setting_preview_size = namespace.add_setting(
|
||||
global_name='DOCUMENTS_PREVIEW_SIZE', default='800'
|
||||
setting_display_height = namespace.add_setting(
|
||||
global_name='DOCUMENTS_DISPLAY_HEIGHT', default=''
|
||||
)
|
||||
setting_print_size = namespace.add_setting(
|
||||
global_name='DOCUMENTS_PRINT_SIZE', default='3600'
|
||||
setting_preview_width = namespace.add_setting(
|
||||
global_name='DOCUMENTS_PREVIEW_WIDTH', default='800'
|
||||
)
|
||||
setting_thumbnail_size = namespace.add_setting(
|
||||
global_name='DOCUMENTS_THUMBNAIL_SIZE', default='800'
|
||||
setting_preview_height = namespace.add_setting(
|
||||
global_name='DOCUMENTS_PREVIEW_HEIGHT', default=''
|
||||
)
|
||||
setting_print_width = namespace.add_setting(
|
||||
global_name='DOCUMENTS_PRINT_WIDTH', default='3600'
|
||||
)
|
||||
setting_print_height = namespace.add_setting(
|
||||
global_name='DOCUMENTS_PRINT_HEIGHT', default=''
|
||||
)
|
||||
setting_thumbnail_width = namespace.add_setting(
|
||||
global_name='DOCUMENTS_THUMBNAIL_WIDTH', default='800'
|
||||
)
|
||||
setting_thumbnail_height = namespace.add_setting(
|
||||
global_name='DOCUMENTS_THUMBNAIL_HEIGHT', default=''
|
||||
)
|
||||
setting_recent_count = namespace.add_setting(
|
||||
global_name='DOCUMENTS_RECENT_COUNT', default=40,
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
{% load i18n %}
|
||||
|
||||
<div class="full-height scrollable" data-height-difference=230 id="carousel-container">
|
||||
{% for document_page in widget.value.pages.all %}
|
||||
<div class="carousel-item" style="height: 100%;">
|
||||
<div class="instance-image-widget">
|
||||
<a href="{% url 'documents:document_page_view' document_page.pk %}">
|
||||
{% with 'lazy-load-carousel' as image_classes %}
|
||||
{% with document_page as instance %}
|
||||
{% with widget.attrs.height as display_height %}
|
||||
{% include 'documents/forms/widgets/document_page_image.html' %}
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
</a>
|
||||
</div>
|
||||
<div class="carousel-item-page-number">
|
||||
{% blocktrans with document_page.page_number as page_number and widget.value.pages.count as total_pages %}
|
||||
Page {{ page_number }} of {{ total_pages }}
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<p>{% trans 'No pages to display' %}</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
@@ -0,0 +1,14 @@
|
||||
<div class="instance-image-widget {{ container_class }}">
|
||||
<div class="spinner-container text-primary"
|
||||
style="height: {% if display_full_height %}100%{% else %}{{ display_height|default:150 }}px{% endif %};"
|
||||
>
|
||||
<i class="far fa-clock fa-2x"></i>
|
||||
</div>
|
||||
<img
|
||||
class="thin_border {{ image_classes }}"
|
||||
data-url="{{ instance.get_api_image_url }}?width={{ image_width }}&height={{ image_height }}&zoom={{ image_zoom }}&rotation={{ image_rotation }}"
|
||||
src="#"
|
||||
style="{% if display_full_width %}width: 100%{% endif %};"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<div class="full-height scrollable mayan-page-wrapper-interactive" data-height-difference=230>
|
||||
{% with 'lazy-load' as image_classes %}
|
||||
{% with widget.attrs.rotation as image_rotation %}
|
||||
{% with widget.attrs.zoom as image_zoom %}
|
||||
{% with widget.value as instance %}
|
||||
{% with 'true' as display_full_height %}
|
||||
{% include 'documents/forms/widgets/document_page_image.html' %}
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<a
|
||||
class="fancybox"
|
||||
{% if disable_title_link %}
|
||||
data-caption="{{ instance }}"
|
||||
{% else %}
|
||||
data-caption="<a class='a-caption' href='{{ instance.get_absolute_url }}'>{{ instance }} <i class='fa fa-external-link-alt'></i></a>"
|
||||
{% endif %}
|
||||
href="{{ instance.get_api_image_url }}?width={{ size_preview_width }}&height={{ size_preview_height }}"
|
||||
{% if gallery_name %}rel="{{ gallery_name }}"{% endif %}
|
||||
>
|
||||
{% with 'lazy-load' as image_classes %}
|
||||
{% with 'true' as display_full_width %}
|
||||
{% include 'documents/forms/widgets/document_page_image.html' %}
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
</a>
|
||||
@@ -41,7 +41,7 @@ from ..permissions import (
|
||||
permission_document_trash, permission_document_view,
|
||||
permission_empty_trash
|
||||
)
|
||||
from ..settings import setting_print_size
|
||||
from ..settings import setting_print_width, setting_print_height
|
||||
from ..tasks import task_delete_document, task_update_page_count
|
||||
from ..utils import parse_range
|
||||
|
||||
@@ -743,7 +743,8 @@ class DocumentPrint(FormView):
|
||||
{
|
||||
'appearance_type': 'plain',
|
||||
'pages': pages,
|
||||
'size': setting_print_size.value,
|
||||
'width': setting_print_width.value,
|
||||
'height': setting_print_height.value,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -2,84 +2,73 @@ from __future__ import unicode_literals
|
||||
|
||||
from django import forms
|
||||
from django.template.loader import render_to_string
|
||||
from django.urls import reverse
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.html import strip_tags
|
||||
from django.utils.http import urlencode
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||
|
||||
from common.utils import index_or_default
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from .settings import (
|
||||
setting_display_size, setting_preview_size, setting_thumbnail_size
|
||||
setting_display_width, setting_display_height, setting_preview_width,
|
||||
setting_preview_height, setting_thumbnail_width, setting_thumbnail_height
|
||||
)
|
||||
|
||||
|
||||
class DocumentPageImageWidget(forms.widgets.Widget):
|
||||
def render(self, name, value, attrs=None):
|
||||
final_attrs = self.build_attrs(attrs)
|
||||
zoom = final_attrs.get('zoom')
|
||||
rotation = final_attrs.get('rotation')
|
||||
template_name = 'documents/forms/widgets/document_page_image_interactive.html'
|
||||
|
||||
html_widget = InteractiveDocumentPageWidget()
|
||||
def __init__(self, attrs=None):
|
||||
default_attrs = {
|
||||
'rotation': 0,
|
||||
'zoom': 100,
|
||||
'width': setting_display_width.value,
|
||||
'height': setting_display_height.value,
|
||||
}
|
||||
if attrs:
|
||||
default_attrs.update(attrs)
|
||||
super(DocumentPageImageWidget, self).__init__(default_attrs)
|
||||
|
||||
if value:
|
||||
output = []
|
||||
output.append(
|
||||
'<div class="full-height scrollable '
|
||||
'mayan-page-wrapper-interactive" data-height-difference=230>'
|
||||
)
|
||||
output.append(
|
||||
html_widget.render(
|
||||
instance=value, zoom=zoom, rotation=rotation,
|
||||
)
|
||||
)
|
||||
output.append('</div>')
|
||||
return mark_safe(''.join(output))
|
||||
else:
|
||||
return ''
|
||||
def format_value(self, value):
|
||||
if value == '' or value is None:
|
||||
return None
|
||||
return value
|
||||
|
||||
|
||||
class DocumentPagesCarouselWidget(forms.widgets.Widget):
|
||||
"""
|
||||
Display many small representations of a document pages
|
||||
Display many small representations of a document's pages
|
||||
"""
|
||||
def render(self, name, value, attrs=None):
|
||||
html_widget = CarouselDocumentPageThumbnailWidget()
|
||||
template_name = 'documents/forms/widgets/document_page_carousel.html'
|
||||
|
||||
output = []
|
||||
output.append(
|
||||
'<div id="carousel-container" class="full-height scrollable" '
|
||||
'data-height-difference=200>'
|
||||
def __init__(self, attrs=None):
|
||||
default_attrs = {
|
||||
'height': setting_preview_height.value,
|
||||
'width': setting_preview_width.value,
|
||||
}
|
||||
|
||||
if attrs:
|
||||
default_attrs.update(attrs)
|
||||
|
||||
super(DocumentPagesCarouselWidget, self).__init__(default_attrs)
|
||||
|
||||
def format_value(self, value):
|
||||
if value == '' or value is None:
|
||||
return None
|
||||
return value
|
||||
|
||||
|
||||
class DocumentPageThumbnailWidget(object):
|
||||
def render(self, instance):
|
||||
return render_to_string(
|
||||
template_name='documents/widgets/document_thumbnail.html',
|
||||
context={
|
||||
'disable_title_link': False,
|
||||
'gallery_name': 'document_list',
|
||||
'instance': instance,
|
||||
'size_preview_width': setting_preview_width.value,
|
||||
'size_preview_height': setting_preview_height.value,
|
||||
'size_thumbnail_width': setting_thumbnail_width.value,
|
||||
'size_thumbnail_height': setting_thumbnail_height.value,
|
||||
}
|
||||
)
|
||||
|
||||
document_pages = value.pages.all()
|
||||
total_pages = value.pages.count()
|
||||
|
||||
for document_page in document_pages:
|
||||
output.append('<div class="carousel-item">')
|
||||
output.append(
|
||||
html_widget.render(instance=document_page)
|
||||
)
|
||||
|
||||
output.append(
|
||||
'<div class="carousel-item-page-number">%s</div>' % ugettext(
|
||||
'Page %(page_number)d of %(total_pages)d'
|
||||
) % {
|
||||
'page_number': document_page.page_number,
|
||||
'total_pages': total_pages
|
||||
}
|
||||
)
|
||||
output.append('</div>')
|
||||
|
||||
if not total_pages:
|
||||
output.append('<p>No pages to display</p>')
|
||||
|
||||
output.append('</div>')
|
||||
|
||||
return mark_safe(''.join(output))
|
||||
|
||||
|
||||
def document_link(document):
|
||||
return mark_safe('<a href="%s">%s</a>' % (
|
||||
@@ -87,311 +76,6 @@ def document_link(document):
|
||||
)
|
||||
|
||||
|
||||
class InstanceImageWidget(object):
|
||||
alt_text = _('Clickable image')
|
||||
click_view_name = None
|
||||
click_view_query_dict = {}
|
||||
destination_view_name = None
|
||||
destination_view_query_dict = {}
|
||||
disable_title_link = False
|
||||
fancybox_class = 'fancybox'
|
||||
gallery_name = None
|
||||
invalid_image_template = 'documents/invalid_document.html'
|
||||
preview_view_name = None
|
||||
preview_query_dict = {}
|
||||
image_class = 'lazy-load'
|
||||
title = None
|
||||
width = None
|
||||
height = None
|
||||
|
||||
# Click view
|
||||
def get_click_view_kwargs(self, instance):
|
||||
"""
|
||||
Determine if the view is a template or API view and vary the view
|
||||
keyword arguments
|
||||
"""
|
||||
|
||||
if self.click_view_name.startswith('rest_api'):
|
||||
return {
|
||||
'pk': instance.document.pk,
|
||||
'version_pk': instance.document_version.pk,
|
||||
'page_pk': instance.pk
|
||||
}
|
||||
else:
|
||||
return {
|
||||
'pk': instance.pk,
|
||||
}
|
||||
|
||||
def get_click_view_query_dict(self, instance):
|
||||
return self.click_view_query_dict
|
||||
|
||||
def get_click_view_querystring(self, instance):
|
||||
return urlencode(self.get_click_view_query_dict(instance=instance))
|
||||
|
||||
def get_click_view_url(self, instance):
|
||||
return '{}?{}'.format(
|
||||
reverse(
|
||||
viewname=self.click_view_name,
|
||||
kwargs=self.get_click_view_kwargs(instance=instance)
|
||||
),
|
||||
self.get_click_view_querystring(instance=instance)
|
||||
)
|
||||
|
||||
# Destination view
|
||||
def get_destination_view_querystring(self, instance):
|
||||
return urlencode(self.get_destination_view_query_dict(instance=instance))
|
||||
|
||||
def get_destination_url(self, instance):
|
||||
return '{}?{}'.format(
|
||||
reverse(
|
||||
viewname=self.destination_view_name,
|
||||
kwargs=self.get_destination_view_kwargs(instance=instance)
|
||||
),
|
||||
self.get_destination_view_querystring(instance=instance)
|
||||
)
|
||||
|
||||
def get_destination_view_kwargs(self, instance):
|
||||
return {
|
||||
'pk': instance.pk
|
||||
}
|
||||
|
||||
# Preview view
|
||||
def get_preview_view_kwargs(self, instance):
|
||||
return {
|
||||
'pk': instance.document.pk,
|
||||
'version_pk': instance.document_version.pk,
|
||||
'page_pk': instance.pk
|
||||
}
|
||||
|
||||
def get_preview_view_query_dict(self, instance):
|
||||
return self.preview_view_query_dict
|
||||
|
||||
def get_preview_view_querystring(self, instance):
|
||||
return urlencode(self.get_preview_view_query_dict(instance=instance))
|
||||
|
||||
def get_preview_view_url(self, instance):
|
||||
return '{}?{}'.format(
|
||||
reverse(
|
||||
viewname=self.preview_view_name,
|
||||
kwargs=self.get_preview_view_kwargs(instance=instance)
|
||||
),
|
||||
self.get_preview_view_querystring(instance=instance)
|
||||
)
|
||||
|
||||
def get_title(self, instance):
|
||||
return self.title
|
||||
|
||||
def is_valid(self, instance):
|
||||
return instance
|
||||
|
||||
def render(self, instance):
|
||||
result = []
|
||||
|
||||
result.append('<div class="instance-image-widget">')
|
||||
|
||||
if not self.is_valid(instance=instance):
|
||||
result.append(
|
||||
render_to_string(
|
||||
self.invalid_image_template
|
||||
)
|
||||
)
|
||||
else:
|
||||
if self.gallery_name:
|
||||
gallery_markup = 'rel="%s"' % self.gallery_name
|
||||
else:
|
||||
gallery_markup = ''
|
||||
|
||||
if self.click_view_name:
|
||||
click_full_url = self.get_click_view_url(instance=instance)
|
||||
|
||||
title = self.get_title(instance=instance)
|
||||
|
||||
if title:
|
||||
if not self.disable_title_link:
|
||||
title_markup = 'data-caption="<a class=\'a-caption\' href=\'{url}\'>{title} <i class=\'fa fa-external-link-alt\'></i></a>"'.format(
|
||||
title=strip_tags(title), url=self.get_destination_url(instance=instance) or '#'
|
||||
)
|
||||
else:
|
||||
title_markup = 'data-caption="{title}"'.format(
|
||||
title=strip_tags(title),
|
||||
)
|
||||
else:
|
||||
title_markup = ''
|
||||
|
||||
result.append(
|
||||
'<a {gallery_markup} class="{fancybox_class}" '
|
||||
'href="{click_full_url}" {title_markup}>'.format(
|
||||
gallery_markup=gallery_markup,
|
||||
fancybox_class=self.fancybox_class,
|
||||
click_full_url=click_full_url,
|
||||
title_markup=title_markup
|
||||
)
|
||||
)
|
||||
|
||||
result.append(
|
||||
'<div class="spinner-container text-primary" style="height: {height}px;">'
|
||||
'<i class="far fa-clock fa-2x"></i>'
|
||||
'</div>'
|
||||
'<img class="thin_border {image_class} pull-left" style="width: {width};"'
|
||||
'data-url="{preview_full_url}" src="#" '
|
||||
'/> '.format(
|
||||
width=self.width or '100%', height=self.height or '150',
|
||||
image_class=self.image_class,
|
||||
preview_full_url=self.get_preview_view_url(instance=instance),
|
||||
alt_text=self.alt_text
|
||||
)
|
||||
)
|
||||
|
||||
if self.click_view_name:
|
||||
result.append('</a>')
|
||||
|
||||
result.append('</div>')
|
||||
|
||||
return mark_safe(''.join(result))
|
||||
|
||||
|
||||
class BaseDocumentThumbnailWidget(InstanceImageWidget):
|
||||
alt_text = _('Document page image')
|
||||
click_view_name = 'rest_api:documentpage-image'
|
||||
click_view_query_dict = {
|
||||
'size': setting_preview_size.value
|
||||
}
|
||||
gallery_name = 'document_list'
|
||||
preview_view_name = 'rest_api:documentpage-image'
|
||||
preview_view_query_dict = {
|
||||
'size': setting_thumbnail_size.value
|
||||
}
|
||||
width = setting_thumbnail_size.value.split('x')[0]
|
||||
height = 150
|
||||
|
||||
def get_destination_url(self, instance):
|
||||
return instance.get_absolute_url()
|
||||
|
||||
|
||||
class CarouselDocumentPageThumbnailWidget(BaseDocumentThumbnailWidget):
|
||||
click_view_name = 'documents:document_page_view'
|
||||
fancybox_class = ''
|
||||
image_class = 'lazy-load-carousel'
|
||||
preview_view_query_dict = {
|
||||
'size': setting_display_size.value
|
||||
}
|
||||
width = setting_preview_size.value.split('x')[0]
|
||||
height = index_or_default(
|
||||
instance=setting_preview_size.value.split('x'),
|
||||
index=1, default=setting_preview_size.value.split('x')[0]
|
||||
)
|
||||
|
||||
|
||||
class DocumentThumbnailWidget(BaseDocumentThumbnailWidget):
|
||||
width = '100%'
|
||||
|
||||
def get_click_view_kwargs(self, instance):
|
||||
return {
|
||||
'pk': instance.pk,
|
||||
'version_pk': instance.latest_version.pk,
|
||||
'page_pk': instance.pages.first().pk
|
||||
}
|
||||
|
||||
def get_click_view_url(self, instance):
|
||||
first_page = instance.pages.first()
|
||||
if first_page:
|
||||
return super(DocumentThumbnailWidget, self).get_click_view_url(
|
||||
instance=instance
|
||||
)
|
||||
else:
|
||||
return '#'
|
||||
|
||||
def get_preview_view_kwargs(self, instance):
|
||||
return {
|
||||
'pk': instance.pk,
|
||||
'version_pk': instance.latest_version.pk,
|
||||
'page_pk': instance.pages.first().pk
|
||||
}
|
||||
|
||||
def get_preview_view_url(self, instance):
|
||||
first_page = instance.pages.first()
|
||||
if first_page:
|
||||
return super(DocumentThumbnailWidget, self).get_preview_view_url(
|
||||
instance=instance
|
||||
)
|
||||
else:
|
||||
return ''
|
||||
|
||||
def get_title(self, instance):
|
||||
return getattr(instance, 'label', None)
|
||||
|
||||
def is_valid(self, instance):
|
||||
return instance.pages
|
||||
|
||||
|
||||
class DocumentPageThumbnailWidget(BaseDocumentThumbnailWidget):
|
||||
width = '100%'
|
||||
|
||||
def get_title(self, instance):
|
||||
return force_text(instance)
|
||||
|
||||
|
||||
class DocumentVersionThumbnailWidget(DocumentThumbnailWidget):
|
||||
width = '100%'
|
||||
|
||||
def get_click_view_kwargs(self, instance):
|
||||
return {
|
||||
'pk': instance.document.pk,
|
||||
'version_pk': instance.pk,
|
||||
'page_pk': instance.pages.first().pk
|
||||
}
|
||||
|
||||
def get_click_view_url(self, instance):
|
||||
first_page = instance.pages.first()
|
||||
if first_page:
|
||||
return super(DocumentVersionThumbnailWidget, self).get_click_view_url(
|
||||
instance=instance
|
||||
)
|
||||
else:
|
||||
return '#'
|
||||
|
||||
def get_preview_view_kwargs(self, instance):
|
||||
return {
|
||||
'pk': instance.document.pk,
|
||||
'version_pk': instance.pk,
|
||||
'page_pk': instance.pages.first().pk
|
||||
}
|
||||
|
||||
def get_preview_view_url(self, instance):
|
||||
first_page = instance.pages.first()
|
||||
if first_page:
|
||||
return super(DocumentVersionThumbnailWidget, self).get_preview_view_url(
|
||||
instance=instance
|
||||
)
|
||||
else:
|
||||
return ''
|
||||
|
||||
def get_title(self, instance):
|
||||
return getattr(instance, 'label', None)
|
||||
|
||||
def is_valid(self, instance):
|
||||
return instance.pages
|
||||
|
||||
|
||||
class InteractiveDocumentPageWidget(BaseDocumentThumbnailWidget):
|
||||
click_view_name = None
|
||||
|
||||
def get_preview_view_query_dict(self, instance):
|
||||
return {
|
||||
'zoom': self.zoom or 100,
|
||||
'rotation': self.rotation or 0,
|
||||
'size': setting_display_size.value,
|
||||
}
|
||||
|
||||
def render(self, instance, *args, **kwargs):
|
||||
self.zoom = kwargs.pop('zoom')
|
||||
self.rotation = kwargs.pop('rotation')
|
||||
|
||||
return super(
|
||||
InteractiveDocumentPageWidget, self
|
||||
).render(instance=instance, *args, **kwargs)
|
||||
|
||||
|
||||
def widget_document_page_number(document):
|
||||
return mark_safe(s=_('Pages: %d') % document.pages.count())
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import time
|
||||
import urllib
|
||||
|
||||
from django.core.files import File
|
||||
from django.urls import reverse
|
||||
from django.utils.encoding import force_text, python_2_unicode_compatible
|
||||
|
||||
from converter import TransformationResize, converter_class
|
||||
@@ -63,6 +64,14 @@ class StagingFile(object):
|
||||
file=open(self.get_full_path(), mode='rb'), name=self.filename
|
||||
)
|
||||
|
||||
def get_api_image_url(self):
|
||||
return reverse(
|
||||
'rest_api:stagingfolderfile-image-view', args=(
|
||||
self.staging_folder.pk,
|
||||
self.encoded_filename
|
||||
)
|
||||
)
|
||||
|
||||
def get_date_time_created(self):
|
||||
return time.ctime(os.path.getctime(self.get_full_path()))
|
||||
|
||||
|
||||
@@ -1,32 +1,25 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from documents.settings import setting_preview_size, setting_thumbnail_size
|
||||
from documents.widgets import BaseDocumentThumbnailWidget
|
||||
from django.template.loader import render_to_string
|
||||
|
||||
from documents.settings import (
|
||||
setting_preview_width, setting_preview_height, setting_thumbnail_width,
|
||||
setting_thumbnail_height
|
||||
)
|
||||
|
||||
|
||||
class StagingFileThumbnailWidget(BaseDocumentThumbnailWidget):
|
||||
disable_title_link = True
|
||||
gallery_name = 'sources:staging_list'
|
||||
click_view_name = 'rest_api:stagingfolderfile-image-view'
|
||||
click_view_query_dict = {
|
||||
'size': setting_preview_size.value
|
||||
}
|
||||
preview_view_name = 'rest_api:stagingfolderfile-image-view'
|
||||
preview_view_query_dict = {
|
||||
'size': setting_thumbnail_size.value
|
||||
}
|
||||
|
||||
def get_click_view_kwargs(self, instance):
|
||||
return {
|
||||
'staging_folder_pk': instance.staging_folder.pk,
|
||||
'encoded_filename': instance.encoded_filename
|
||||
}
|
||||
|
||||
def get_preview_view_kwargs(self, instance):
|
||||
return {
|
||||
'staging_folder_pk': instance.staging_folder.pk,
|
||||
'encoded_filename': instance.encoded_filename
|
||||
}
|
||||
|
||||
def get_title(self, instance):
|
||||
return instance.filename
|
||||
class StagingFileThumbnailWidget(object):
|
||||
def render(self, instance):
|
||||
return render_to_string(
|
||||
template_name='documents/widgets/document_thumbnail.html',
|
||||
context={
|
||||
'container_class': 'staging-file-thumbnail-container',
|
||||
'disable_title_link': True,
|
||||
'gallery_name': 'sources:staging_list',
|
||||
'instance': instance,
|
||||
'size_preview_width': setting_preview_width.value,
|
||||
'size_preview_height': setting_preview_height.value,
|
||||
'size_thumbnail_width': setting_thumbnail_width.value,
|
||||
'size_thumbnail_height': setting_thumbnail_height.value,
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user