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:
Michael Price
2018-03-12 15:55:13 -04:00
committed by Roberto Rosario
parent d29d4ba110
commit 8590bff6e4
19 changed files with 264 additions and 471 deletions

View File

@@ -33,7 +33,12 @@ Next (2018-XX-XX)
- Documents without at least a version are not scanned for duplicates. - 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. - 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. - 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) 2.8 (2018-02-27)
================ ================
- Rename the role groups link label from "Members" to "Groups". - Rename the role groups link label from "Members" to "Groups".

View File

@@ -27,4 +27,3 @@ class Icon(object):
} }
) )
) )

View File

@@ -120,6 +120,7 @@ img.lazy-load-carousel {
.instance-image-widget { .instance-image-widget {
text-align: center; text-align: center;
height: 100%;
} }
hr { hr {
@@ -700,3 +701,9 @@ a i {
border-left: 6px solid transparent; border-left: 6px solid transparent;
border-right: 6px solid transparent; border-right: 6px solid transparent;
border-bottom: 6px solid #be2626; } border-bottom: 6px solid #be2626; }
.staging-file-thumbnail-container {
width: 150px;
margin: auto;
}

View File

@@ -98,13 +98,6 @@ def get_descriptor(file_input, read=True):
return file_input return file_input
def index_or_default(instance, index, default):
try:
return instance[index]
except IndexError:
return default
def TemporaryFile(*args, **kwargs): def TemporaryFile(*args, **kwargs):
kwargs.update({'dir': setting_temporary_directory.value}) kwargs.update({'dir': setting_temporary_directory.value})
return tempfile.TemporaryFile(*args, **kwargs) return tempfile.TemporaryFile(*args, **kwargs)

View File

@@ -10,5 +10,3 @@ DEFAULT_PDFTOPPM_DPI = 300
DEFAULT_PDFTOPPM_FORMAT = 'jpeg' # Possible values jpeg, png, tiff DEFAULT_PDFTOPPM_FORMAT = 'jpeg' # Possible values jpeg, png, tiff
DEFAULT_PDFTOPPM_PATH = '/usr/bin/pdftoppm' DEFAULT_PDFTOPPM_PATH = '/usr/bin/pdftoppm'
DEFAULT_PDFINFO_PATH = '/usr/bin/pdfinfo' DEFAULT_PDFINFO_PATH = '/usr/bin/pdfinfo'
DIMENSION_SEPARATOR = 'x'

View File

@@ -337,8 +337,12 @@ class APIDocumentPageImageView(generics.RetrieveAPIView):
GET: GET:
omit_serializer: true omit_serializer: true
parameters: parameters:
- name: size - name: width
description: 'x' seprated width and height of the desired image representation. description: Width of the desired image representation.
paramType: query
type: number
- name: height
description: Height of the desired image representation.
paramType: query paramType: query
type: number type: number
- name: zoom - name: zoom
@@ -377,7 +381,8 @@ class APIDocumentPageImageView(generics.RetrieveAPIView):
return None return None
def retrieve(self, request, *args, **kwargs): 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') zoom = request.GET.get('zoom')
if zoom: if zoom:
@@ -390,8 +395,8 @@ class APIDocumentPageImageView(generics.RetrieveAPIView):
task = task_generate_document_page_image.apply_async( task = task_generate_document_page_image.apply_async(
kwargs=dict( kwargs=dict(
document_page_id=self.get_object().pk, size=size, zoom=zoom, document_page_id=self.get_object().pk, width=width,
rotation=rotation height=height, zoom=zoom, rotation=rotation
) )
) )

View File

@@ -101,8 +101,7 @@ from .statistics import (
total_document_page_per_month, total_document_version_per_month total_document_page_per_month, total_document_version_per_month
) )
from .widgets import ( from .widgets import (
DocumentThumbnailWidget, DocumentPageThumbnailWidget, DocumentPageThumbnailWidget, widget_document_page_number,
DocumentVersionThumbnailWidget, widget_document_page_number,
widget_document_version_page_number widget_document_version_page_number
) )
@@ -207,13 +206,11 @@ class DocumentsApp(MayanAppConfig):
# Document and document page thumbnail widget # Document and document page thumbnail widget
document_page_thumbnail_widget = DocumentPageThumbnailWidget() document_page_thumbnail_widget = DocumentPageThumbnailWidget()
document_thumbnail_widget = DocumentThumbnailWidget()
document_version_thumbnail_widget = DocumentVersionThumbnailWidget()
# Document # Document
SourceColumn( SourceColumn(
source=Document, label=_('Thumbnail'), source=Document, label=_('Thumbnail'),
func=lambda context: document_thumbnail_widget.render( func=lambda context: document_page_thumbnail_widget.render(
instance=context['object'] instance=context['object']
) )
) )
@@ -263,7 +260,7 @@ class DocumentsApp(MayanAppConfig):
# DeletedDocument # DeletedDocument
SourceColumn( SourceColumn(
source=DeletedDocument, label=_('Thumbnail'), source=DeletedDocument, label=_('Thumbnail'),
func=lambda context: document_thumbnail_widget.render( func=lambda context: document_page_thumbnail_widget.render(
instance=context['object'] instance=context['object']
) )
) )
@@ -279,7 +276,7 @@ class DocumentsApp(MayanAppConfig):
# DocumentVersion # DocumentVersion
SourceColumn( SourceColumn(
source=DocumentVersion, label=_('Thumbnail'), source=DocumentVersion, label=_('Thumbnail'),
func=lambda context: document_version_thumbnail_widget.render( func=lambda context: document_page_thumbnail_widget.render(
instance=context['object'] instance=context['object']
) )
) )
@@ -309,7 +306,7 @@ class DocumentsApp(MayanAppConfig):
# DuplicatedDocument # DuplicatedDocument
SourceColumn( SourceColumn(
source=DuplicatedDocument, label=_('Thumbnail'), source=DuplicatedDocument, label=_('Thumbnail'),
func=lambda context: document_thumbnail_widget.render( func=lambda context: document_page_thumbnail_widget.render(
instance=context['object'].document instance=context['object'].document
) )
) )

View 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

View File

@@ -9,35 +9,34 @@ from django.utils.translation import ugettext_lazy as _
from acls.models import AccessControlList from acls.models import AccessControlList
from common.forms import DetailForm from common.forms import DetailForm
from .fields import (
DocumentField, DocumentPageField, DocumentVersionField
)
from .models import ( from .models import (
Document, DocumentType, DocumentPage, DocumentTypeFilename Document, DocumentType, DocumentTypeFilename
) )
from .literals import DEFAULT_ZIP_FILENAME, PAGE_RANGE_ALL, PAGE_RANGE_CHOICES from .literals import DEFAULT_ZIP_FILENAME, PAGE_RANGE_ALL, PAGE_RANGE_CHOICES
from .permissions import permission_document_create from .permissions import permission_document_create
from .settings import setting_language_choices from .settings import setting_language_choices
from .widgets import DocumentPagesCarouselWidget, DocumentPageImageWidget
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Document page forms # Document page forms
class DocumentPageForm(DetailForm):
class Meta:
fields = ()
model = DocumentPage
class DocumentPageForm(forms.Form):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
zoom = kwargs.pop('zoom', None) instance = kwargs.pop('instance', None)
rotation = kwargs.pop('rotation', None) rotation = kwargs.pop('rotation', None)
zoom = kwargs.pop('zoom', None)
super(DocumentPageForm, self).__init__(*args, **kwargs) super(DocumentPageForm, self).__init__(*args, **kwargs)
self.fields['page_image'].initial = self.instance self.fields['document_page'].initial = instance
self.fields['page_image'].widget.attrs.update({ self.fields['document_page'].widget.attrs.update({
'zoom': zoom, 'zoom': zoom,
'rotation': rotation 'rotation': rotation,
}) })
page_image = forms.CharField( document_page = DocumentPageField()
label=_('Page image'), widget=DocumentPageImageWidget()
)
# Document forms # Document forms
@@ -45,31 +44,18 @@ class DocumentPreviewForm(forms.Form):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
document = kwargs.pop('instance', None) document = kwargs.pop('instance', None)
super(DocumentPreviewForm, self).__init__(*args, **kwargs) super(DocumentPreviewForm, self).__init__(*args, **kwargs)
self.fields['preview'].initial = document self.fields['document'].initial = document
try:
self.fields['preview'].label = _(
'Document pages (%d)'
) % document.page_count
except AttributeError:
self.fields['preview'].label = _('Document pages (%d)') % 0
preview = forms.CharField(widget=DocumentPagesCarouselWidget()) document = DocumentField()
class DocumentVersionPreviewForm(forms.Form): class DocumentVersionPreviewForm(forms.Form):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
document_version = kwargs.pop('instance', None) document_version = kwargs.pop('instance', None)
super(DocumentVersionPreviewForm, self).__init__(*args, **kwargs) super(DocumentVersionPreviewForm, self).__init__(*args, **kwargs)
self.fields['document_version'].initial = document_version
self.fields['preview'].initial = document_version document_version = DocumentVersionField()
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())
class DocumentForm(forms.ModelForm): class DocumentForm(forms.ModelForm):

View File

@@ -39,8 +39,8 @@ from .permissions import permission_document_view
from .runtime import cache_storage_backend, storage_backend from .runtime import cache_storage_backend, storage_backend
from .settings import ( from .settings import (
setting_disable_base_image_cache, setting_disable_transformed_image_cache, setting_disable_base_image_cache, setting_disable_transformed_image_cache,
setting_display_size, setting_language, setting_zoom_max_level, setting_display_width, setting_display_height, setting_language,
setting_zoom_min_level setting_zoom_max_level, setting_zoom_min_level
) )
from .signals import ( from .signals import (
post_document_created, post_document_type_change, post_version_upload post_document_created, post_document_type_change, post_version_upload
@@ -195,6 +195,11 @@ class Document(models.Model):
passthrough = PassthroughManager() passthrough = PassthroughManager()
trash = TrashCanManager() trash = TrashCanManager()
class Meta:
verbose_name = _('Document')
verbose_name_plural = _('Documents')
ordering = ('-date_added',)
def __str__(self): def __str__(self):
return self.label or ugettext('Document stub, id: %d') % self.pk return self.label or ugettext('Document stub, id: %d') % self.pk
@@ -238,11 +243,6 @@ class Document(models.Model):
if _commit_events: if _commit_events:
event_document_properties_edit.commit(actor=user, target=self) 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): def add_as_recent_document_for_user(self, user):
return RecentDocument.objects.add_document_for_user(user, self) return RecentDocument.objects.add_document_for_user(user, self)
@@ -257,6 +257,11 @@ class Document(models.Model):
else: else:
return False 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): def invalidate_cache(self):
for document_version in self.versions.all(): for document_version in self.versions.all():
document_version.invalidate_cache() document_version.invalidate_cache()
@@ -421,6 +426,11 @@ class DocumentVersion(models.Model):
def get_absolute_url(self): def get_absolute_url(self):
return reverse('documents:document_version_view', args=(self.pk,)) 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): def get_rendered_string(self, preserve_extension=False):
if preserve_extension: if preserve_extension:
filename, extension = os.path.splitext(self.document.label) 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 # Set sensible defaults if the argument is not specified or if the
# argument is None # 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 rotation = kwargs.get('rotation', DEFAULT_ROTATION) or DEFAULT_ROTATION
zoom_level = kwargs.get('zoom', DEFAULT_ZOOM_LEVEL) or DEFAULT_ZOOM_LEVEL zoom_level = kwargs.get('zoom', DEFAULT_ZOOM_LEVEL) or DEFAULT_ZOOM_LEVEL
@@ -785,11 +796,9 @@ class DocumentPage(models.Model):
TransformationRotate(degrees=rotation) TransformationRotate(degrees=rotation)
) )
if size: if width:
transformation_list.append( transformation_list.append(
TransformationResize( TransformationResize(width=width, height=height)
**dict(zip(('width', 'height'), (size.split('x'))))
)
) )
if zoom_level: if zoom_level:
@@ -875,6 +884,13 @@ class DocumentPage(models.Model):
""" """
return '{}-{}'.format(self.document_version.uuid, self.pk) 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): class DocumentPageCachedImage(models.Model):
document_page = models.ForeignKey( document_page = models.ForeignKey(

View File

@@ -10,18 +10,31 @@ LANGUAGE_CHOICES = [
(i.iso639_3_code, i.name) for i in list(pycountry.languages) (i.iso639_3_code, i.name) for i in list(pycountry.languages)
] ]
namespace = Namespace(name='documents', label=_('Documents')) namespace = Namespace(name='documents', label=_('Documents'))
setting_display_size = namespace.add_setting( setting_display_width = namespace.add_setting(
global_name='DOCUMENTS_DISPLAY_SIZE', default='3600' global_name='DOCUMENTS_DISPLAY_WIDTH', default='3600'
) )
setting_preview_size = namespace.add_setting( setting_display_height = namespace.add_setting(
global_name='DOCUMENTS_PREVIEW_SIZE', default='800' global_name='DOCUMENTS_DISPLAY_HEIGHT', default=''
) )
setting_print_size = namespace.add_setting( setting_preview_width = namespace.add_setting(
global_name='DOCUMENTS_PRINT_SIZE', default='3600' global_name='DOCUMENTS_PREVIEW_WIDTH', default='800'
) )
setting_thumbnail_size = namespace.add_setting( setting_preview_height = namespace.add_setting(
global_name='DOCUMENTS_THUMBNAIL_SIZE', default='800' 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( setting_recent_count = namespace.add_setting(
global_name='DOCUMENTS_RECENT_COUNT', default=40, global_name='DOCUMENTS_RECENT_COUNT', default=40,

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -41,7 +41,7 @@ from ..permissions import (
permission_document_trash, permission_document_view, permission_document_trash, permission_document_view,
permission_empty_trash 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 ..tasks import task_delete_document, task_update_page_count
from ..utils import parse_range from ..utils import parse_range
@@ -743,7 +743,8 @@ class DocumentPrint(FormView):
{ {
'appearance_type': 'plain', 'appearance_type': 'plain',
'pages': pages, 'pages': pages,
'size': setting_print_size.value, 'width': setting_print_width.value,
'height': setting_print_height.value,
} }
) )

View File

@@ -2,84 +2,73 @@ from __future__ import unicode_literals
from django import forms from django import forms
from django.template.loader import render_to_string 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.safestring import mark_safe
from django.utils.translation import ugettext, ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from common.utils import index_or_default
from .settings import ( 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): class DocumentPageImageWidget(forms.widgets.Widget):
def render(self, name, value, attrs=None): template_name = 'documents/forms/widgets/document_page_image_interactive.html'
final_attrs = self.build_attrs(attrs)
zoom = final_attrs.get('zoom')
rotation = final_attrs.get('rotation')
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: def format_value(self, value):
output = [] if value == '' or value is None:
output.append( return None
'<div class="full-height scrollable ' return value
'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 ''
class DocumentPagesCarouselWidget(forms.widgets.Widget): 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): template_name = 'documents/forms/widgets/document_page_carousel.html'
html_widget = CarouselDocumentPageThumbnailWidget()
output = [] def __init__(self, attrs=None):
output.append( default_attrs = {
'<div id="carousel-container" class="full-height scrollable" ' 'height': setting_preview_height.value,
'data-height-difference=200>' '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): def document_link(document):
return mark_safe('<a href="%s">%s</a>' % ( 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): def widget_document_page_number(document):
return mark_safe(s=_('Pages: %d') % document.pages.count()) return mark_safe(s=_('Pages: %d') % document.pages.count())

View File

@@ -7,6 +7,7 @@ import time
import urllib import urllib
from django.core.files import File from django.core.files import File
from django.urls import reverse
from django.utils.encoding import force_text, python_2_unicode_compatible from django.utils.encoding import force_text, python_2_unicode_compatible
from converter import TransformationResize, converter_class from converter import TransformationResize, converter_class
@@ -63,6 +64,14 @@ class StagingFile(object):
file=open(self.get_full_path(), mode='rb'), name=self.filename 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): def get_date_time_created(self):
return time.ctime(os.path.getctime(self.get_full_path())) return time.ctime(os.path.getctime(self.get_full_path()))

View File

@@ -1,32 +1,25 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from documents.settings import setting_preview_size, setting_thumbnail_size from django.template.loader import render_to_string
from documents.widgets import BaseDocumentThumbnailWidget
from documents.settings import (
setting_preview_width, setting_preview_height, setting_thumbnail_width,
setting_thumbnail_height
)
class StagingFileThumbnailWidget(BaseDocumentThumbnailWidget): class StagingFileThumbnailWidget(object):
disable_title_link = True def render(self, instance):
gallery_name = 'sources:staging_list' return render_to_string(
click_view_name = 'rest_api:stagingfolderfile-image-view' template_name='documents/widgets/document_thumbnail.html',
click_view_query_dict = { context={
'size': setting_preview_size.value 'container_class': 'staging-file-thumbnail-container',
} 'disable_title_link': True,
preview_view_name = 'rest_api:stagingfolderfile-image-view' 'gallery_name': 'sources:staging_list',
preview_view_query_dict = { 'instance': instance,
'size': setting_thumbnail_size.value 'size_preview_width': setting_preview_width.value,
} 'size_preview_height': setting_preview_height.value,
'size_thumbnail_width': setting_thumbnail_width.value,
def get_click_view_kwargs(self, instance): 'size_thumbnail_height': setting_thumbnail_height.value,
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