Manual merge of the new document preview code from the unstable branch

This commit is contained in:
Roberto Rosario
2014-07-02 02:53:53 -04:00
parent 77ff0f7191
commit 0cda1148bb
139 changed files with 6330 additions and 2349 deletions

View File

@@ -16,6 +16,7 @@ from navigation.api import (register_links, register_top_menu,
from project_setup.api import register_setup
from .conf import settings as document_settings
from .conf.settings import THUMBNAIL_SIZE
from .events import (HISTORY_DOCUMENT_CREATED,
HISTORY_DOCUMENT_EDITED, HISTORY_DOCUMENT_DELETED)
from .links import (document_list, document_list_recent,
@@ -92,11 +93,10 @@ register_diagnostic('documents', _(u'Documents'), document_missing_list)
register_maintenance_links([document_find_all_duplicates, document_update_page_count, document_clear_image_cache], namespace='documents', title=_(u'documents'))
register_model_list_columns(Document, [
{
'name': _(u'thumbnail'), 'attribute':
encapsulate(lambda x: document_thumbnail(x))
},
])
{'name': _(u'thumbnail'), 'attribute':
encapsulate(lambda x: document_thumbnail(x, gallery_name='document_list', title=x.filename, size=THUMBNAIL_SIZE))
},
])
register_top_menu(
'documents',

View File

@@ -0,0 +1,71 @@
from __future__ import absolute_import
import logging
from django.core.exceptions import PermissionDenied
from django.shortcuts import get_object_or_404
from rest_framework import generics
from rest_framework.generics import RetrieveAPIView
from rest_framework.response import Response
from acls.models import AccessEntry
from converter.exceptions import UnknownFileFormat, UnkownConvertError
from converter.literals import (DEFAULT_ZOOM_LEVEL, DEFAULT_ROTATION,
DEFAULT_PAGE_NUMBER)
from permissions.models import Permission
from .conf.settings import DISPLAY_SIZE, ZOOM_MAX_LEVEL, ZOOM_MIN_LEVEL
from .models import Document
from .permissions import PERMISSION_DOCUMENT_VIEW
from .resources import DocumentResourceSimple
logger = logging.getLogger(__name__)
# API Views
class APIReadOnlyInstanceModelView(RetrieveAPIView):
allowed_methods = ['GET']
serializer_class = DocumentResourceSimple
queryset = Document.objects.all()
class APIDocumentImageView(generics.GenericAPIView):
def get(self, request, pk):
document = get_object_or_404(Document, pk=pk)
logger.debug('document: %s' % document)
try:
Permission.objects.check_permissions(request.user, [PERMISSION_DOCUMENT_VIEW])
except PermissionDenied:
AccessEntry.objects.check_access(PERMISSION_DOCUMENT_VIEW, request.user, document)
size = request.GET.get('size', DISPLAY_SIZE)
page = int(request.GET.get('page', DEFAULT_PAGE_NUMBER))
zoom = int(request.GET.get('zoom', DEFAULT_ZOOM_LEVEL))
version = int(request.GET.get('version', document.latest_version.pk))
if request.GET.get('as_base64', False):
base64_version = True
if zoom < ZOOM_MIN_LEVEL:
zoom = ZOOM_MIN_LEVEL
if zoom > ZOOM_MAX_LEVEL:
zoom = ZOOM_MAX_LEVEL
rotation = int(request.GET.get('rotation', DEFAULT_ROTATION)) % 360
try:
return Response({'status': 'success',
'data': document.get_image(size=size, page=page, zoom=zoom, rotation=rotation, as_base64=True, version=version)
})
except UnknownFileFormat as exception:
return Response({'status': 'error', 'detail': 'unknown_file_format', 'message': unicode(exception)})
except UnkownConvertError as exception:
return Response({'status': 'error', 'detail': 'converter_error', 'message': unicode(exception)})

View File

@@ -1,7 +1,6 @@
from __future__ import absolute_import
from django import forms
from django.core.urlresolvers import reverse
from django.utils.encoding import force_unicode
from django.utils.html import conditional_escape
from django.utils.safestring import mark_safe
@@ -16,7 +15,7 @@ from .models import (Document, DocumentType,
DocumentVersion)
from .literals import (RELEASE_LEVEL_FINAL, RELEASE_LEVEL_CHOICES,
DEFAULT_ZIP_FILENAME)
from .widgets import document_html_widget
from .widgets import DocumentPageImageWidget, DocumentPagesCarouselWidget
# Document page forms
@@ -29,26 +28,11 @@ class DocumentPageTransformationForm(forms.ModelForm):
self.fields['document_page'].widget = forms.HiddenInput()
class DocumentPageImageWidget(forms.widgets.Widget):
def render(self, name, value, attrs=None):
final_attrs = self.build_attrs(attrs)
zoom = final_attrs.get('zoom', 100)
rotation = final_attrs.get('rotation', 0)
if value:
output = []
output.append('<div class="full-height scrollable" style="overflow: auto;">')
output.append(document_html_widget(value.document, view='document_display', page=value.page_number, zoom=zoom, rotation=rotation))
output.append('</div>')
return mark_safe(u''.join(output))
else:
return u''
class DocumentPageForm(DetailForm):
class Meta:
model = DocumentPage
exclude = ('document', 'document_type', 'page_label', 'content')
#exclude = ('document', 'document_type', 'page_label', 'content')
fields = ()
def __init__(self, *args, **kwargs):
zoom = kwargs.pop('zoom', 100)
@@ -96,37 +80,6 @@ class DocumentPageForm_edit(forms.ModelForm):
# Document forms
class DocumentPagesCarouselWidget(forms.widgets.Widget):
"""
Display many small representations of a document pages
"""
def render(self, name, value, attrs=None):
output = []
output.append(u'<div style="white-space:nowrap; overflow: auto;">')
for page in value.pages.all():
output.append(u'<div style="display: inline-block; margin: 5px 10px 10px 10px;">')
output.append(u'<div class="tc">%(page_string)s %(page)s</div>' % {'page_string': ugettext(u'Page'), 'page': page.page_number})
output.append(
document_html_widget(
page.document,
view='document_preview_multipage',
click_view='document_display',
page=page.page_number,
gallery_name='document_pages',
fancybox_class='fancybox-noscaling',
)
)
output.append(u'<div class="tc">')
output.append(u'<a class="fancybox-iframe" href="%s"><span class="famfam active famfam-page_white_go"></span>%s</a>' % (reverse('document_page_view', args=[page.pk]), ugettext(u'Details')))
output.append(u'</div>')
output.append(u'</div>')
output.append(u'</div>')
output.append(u'<br /><span class="famfam active famfam-magnifier"></span>%s' % ugettext(u'Click on the image for full size preview'))
return mark_safe(u''.join(output))
class DocumentPreviewForm(forms.Form):
@@ -146,7 +99,8 @@ class DocumentForm(forms.ModelForm):
"""
class Meta:
model = Document
exclude = ('description', 'tags', 'document_type')
#exclude = ('description', 'tags', 'document_type')
exclude = ('tags', 'document_type')
def __init__(self, *args, **kwargs):
document_type = kwargs.pop('document_type', None)

View File

@@ -126,6 +126,9 @@ class Document(models.Model):
if not version:
version = self.latest_version.pk
image_cache_name = self.get_image_cache_name(page=page, version=version)
logger.debug('image_cache_name: %s' % image_cache_name)
return convert(image_cache_name, cleanup_files=False, size=size, zoom=zoom, rotation=rotation)
def get_image(self, size=DISPLAY_SIZE, page=DEFAULT_PAGE_NUMBER, zoom=DEFAULT_ZOOM_LEVEL, rotation=DEFAULT_ROTATION, as_base64=False, version=None):
@@ -137,20 +140,12 @@ class Document(models.Model):
rotation = rotation % 360
try:
file_path = self.get_valid_image(size=size, page=page, zoom=zoom, rotation=rotation, version=version)
except UnknownFileFormat:
file_path = get_icon_file_path(self.file_mimetype)
except UnkownConvertError:
file_path = get_error_icon_file_path()
except:
file_path = get_error_icon_file_path()
file_path = self.get_valid_image(size=size, page=page, zoom=zoom, rotation=rotation, version=version)
logger.debug('file_path: %s' % file_path)
if as_base64:
image = open(file_path, 'r')
out = StringIO()
base64.encode(image, out)
return u'data:%s;base64,%s' % (get_mimetype(open(file_path, 'r'), file_path, mimetype_only=True)[0], out.getvalue().replace('\n', ''))
return u'data:%s;base64,%s' % (get_mimetype(open(file_path, 'r'), file_path, mimetype_only=True)[0], base64.b64encode(image.read()))
else:
return file_path

View File

@@ -1,6 +1,9 @@
from __future__ import absolute_import
from django.conf.urls import url
from .cleanup import cleanup
from .api import APIReadOnlyInstanceModelView, APIDocumentImageView
bootstrap_models = [
{
@@ -12,3 +15,8 @@ bootstrap_models = [
}
]
cleanup_functions = [cleanup]
version_0_api_services = [
{'urlpattern': url(r'^document/(?P<pk>[0-9]+)/$', APIReadOnlyInstanceModelView.as_view(), name='document-detail'), 'description': 'Show document data', 'url': 'document/<document_id>'},
{'urlpattern': url(r'^document/(?P<pk>[0-9]+)/image/$', APIDocumentImageView.as_view(), name='document-image'), 'description': 'Return a base64 image of the document', 'url': 'document/<document_id>/image/?page&zoom&rotate'},
]

View File

@@ -0,0 +1,45 @@
from __future__ import absolute_import
from django.core.urlresolvers import reverse
from rest_framework import serializers
from .models import Document
class DocumentResourceSimple(serializers.HyperlinkedModelSerializer):
def versions(self, instance):
return [
{
'version': version.get_formated_version(),
'major': version.major,
'minor': version.minor,
'micro': version.micro,
'release_level': version.release_level,
'serial': version.serial,
'timestamp': version.timestamp,
'comment': version.comment,
'mimetype': version.mimetype,
'encoding': version.encoding,
'filename': version.filename,
'checksum': version.checksum,
'download': reverse('document_version_download', args=[version.pk]),
'stored_filename': version.file.name,
# TODO: Add transformations
'pages': [
{
'content': page.content,
'page_label': page.page_label,
'page_number': page.page_number,
}
for page in version.pages.all()
]
}
for version in instance.versions.all()
]
class Meta:
model = Document
#fields = ('url', 'pk', 'document_type', 'uuid', 'date_added', 'description', 'expensive_methods', 'versions')
fields = ('url', 'document_type', 'uuid', 'date_added', 'description')#, 'versions')

View File

@@ -8,7 +8,7 @@ from django.conf import settings
from django.contrib import messages
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect, HttpResponse
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
from django.utils.http import urlencode
@@ -24,6 +24,7 @@ from common.utils import (pretty_size, parse_range, urlquote,
return_diff, encapsulate)
from common.widgets import two_state_template
from common.conf.settings import DEFAULT_PAPER_SIZE
from converter.exceptions import UnknownFileFormat, UnkownConvertError
from converter.literals import (DEFAULT_ZOOM_LEVEL, DEFAULT_ROTATION,
DEFAULT_PAGE_NUMBER, DEFAULT_FILE_FORMAT_MIMETYPE)
from converter.office_converter import OfficeConverter
@@ -33,7 +34,7 @@ from navigation.utils import resolve_to_name
from permissions.models import Permission
from .events import HISTORY_DOCUMENT_EDITED
from .conf.settings import (PREVIEW_SIZE, STORAGE_BACKEND, ZOOM_PERCENT_STEP,
from .conf.settings import (DISPLAY_SIZE, PREVIEW_SIZE, STORAGE_BACKEND, ZOOM_PERCENT_STEP,
ZOOM_MAX_LEVEL, ZOOM_MIN_LEVEL, ROTATION_STEP, RECENT_COUNT)
from .forms import (DocumentForm_edit, DocumentPropertiesForm,
DocumentPreviewForm, DocumentPageForm,
@@ -246,7 +247,7 @@ def document_edit(request, document_id):
}, context_instance=RequestContext(request))
def get_document_image(request, document_id, size=PREVIEW_SIZE, base64_version=False):
def get_document_image(request, document_id, size=PREVIEW_SIZE):
document = get_object_or_404(Document, pk=document_id)
try:
Permission.objects.check_permissions(request.user, [PERMISSION_DOCUMENT_VIEW])
@@ -267,11 +268,7 @@ def get_document_image(request, document_id, size=PREVIEW_SIZE, base64_version=F
rotation = int(request.GET.get('rotation', DEFAULT_ROTATION)) % 360
if base64_version:
return HttpResponse(u'<html><body><img src="%s" /></body></html>' % document.get_image(size=size, page=page, zoom=zoom, rotation=rotation, as_base64=True, version=version))
else:
# TODO: fix hardcoded MIMETYPE
return sendfile.sendfile(request, document.get_image(size=size, page=page, zoom=zoom, rotation=rotation, version=version), mimetype=DEFAULT_FILE_FORMAT_MIMETYPE)
return sendfile.sendfile(request, document.get_image(size=size, page=page, zoom=zoom, rotation=rotation, version=version), mimetype=DEFAULT_FILE_FORMAT_MIMETYPE)
def document_download(request, document_id=None, document_id_list=None, document_version_pk=None):

View File

@@ -1,25 +1,78 @@
from __future__ import absolute_import
from django.utils.safestring import mark_safe
from django import forms
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.core.urlresolvers import reverse
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
from django.utils.translation import ugettext_lazy as _
from converter.literals import (DEFAULT_ZOOM_LEVEL, DEFAULT_ROTATION,
DEFAULT_PAGE_NUMBER)
from mimetype.api import get_error_icon_url
from .conf.settings import (THUMBNAIL_SIZE,
DISPLAY_SIZE, MULTIPAGE_PREVIEW_SIZE)
def document_thumbnail(document):
return document_html_widget(document, click_view='document_preview')
class DocumentPageImageWidget(forms.widgets.Widget):
def render(self, name, value, attrs=None):
final_attrs = self.build_attrs(attrs)
zoom = final_attrs.get('zoom', 100)
rotation = final_attrs.get('rotation', 0)
if value:
output = []
output.append('<div class="full-height scrollable" style="overflow: auto;" >')
output.append(document_html_widget(value.document, page=value.page_number, zoom=zoom, rotation=rotation, image_class='lazy-load-interactive', nolazyload=False, size=DISPLAY_SIZE))
output.append('</div>')
return mark_safe(u''.join(output))
else:
return u''
class DocumentPagesCarouselWidget(forms.widgets.Widget):
"""
Display many small representations of a document pages
"""
def render(self, name, value, attrs=None):
output = []
output.append(u'<div class="carousel-container" style="white-space:nowrap; overflow: auto;">')
for page in value.pages.all():
output.append(u'<div style="display: inline-block; margin: 5px 10px 10px 10px;">')
output.append(u'<div class="tc">%(page_string)s %(page)s</div>' % {'page_string': ugettext(u'Page'), 'page': page.page_number})
output.append(
document_html_widget(
page.document,
click_view='document_display',
page=page.page_number,
gallery_name='document_pages',
fancybox_class='fancybox-noscaling',
image_class='lazy-load-carousel',
title=ugettext(u'Page %(page_num)d of %(total_pages)d') % {'page_num': page.page_number, 'total_pages': len(value.pages.all())},
size=MULTIPAGE_PREVIEW_SIZE,
)
)
output.append(u'<div class="tc">')
output.append(u'<a class="fancybox-iframe" href="%s">%s%s</a>' % (reverse('document_page_view', args=[page.pk]), '<span class="famfam active famfam-page_white_go"></span>', ugettext(u'Details')))
output.append(u'</div></div>')
output.append(u'</div><br />%s%s' % ('<span class="famfam active famfam-page_white_magnify"></span>', ugettext(u'Click on the image for full size preview')))
return mark_safe(u''.join(output))
def document_thumbnail(document, **kwargs):
return document_html_widget(document, click_view='document_preview', **kwargs)
def document_link(document):
return mark_safe(u'<a href="%s">%s</a>' % (reverse('document_view_simple', args=[document.pk]), document))
def document_html_widget(document, view='document_thumbnail', click_view=None, page=DEFAULT_PAGE_NUMBER, zoom=DEFAULT_ZOOM_LEVEL, rotation=DEFAULT_ROTATION, gallery_name=None, fancybox_class='fancybox', version=None):
def document_html_widget(document, click_view=None, page=DEFAULT_PAGE_NUMBER, zoom=DEFAULT_ZOOM_LEVEL, rotation=DEFAULT_ROTATION, gallery_name=None, fancybox_class='fancybox', version=None, image_class='lazy-load', title=None, size=THUMBNAIL_SIZE, nolazyload=False):
result = []
alt_text = _(u'document page image')
@@ -32,6 +85,7 @@ def document_html_widget(document, view='document_thumbnail', click_view=None, p
'zoom': zoom,
'rotation': rotation,
'version': version,
'size': size,
}
if gallery_name:
@@ -40,43 +94,30 @@ def document_html_widget(document, view='document_thumbnail', click_view=None, p
gallery_template = u''
query_string = urlencode(query_dict)
preview_view = u'%s?%s' % (reverse(view, args=[document.pk]), query_string)
preview_view = u'%s?%s' % (reverse('document-image', args=[document.pk]), query_string)
plain_template = []
plain_template.append(u'<img src="%s" alt="%s" />' % (preview_view, alt_text))
result.append(u'<div class="tc" id="document-%d-%d">' % (document.pk, page if page else 1))
if title:
title_template = u'title="%s"' % strip_tags(title)
else:
title_template = u''
if click_view:
result.append(u'<a %s class="%s" href="%s">' % (gallery_template, fancybox_class, u'%s?%s' % (reverse(click_view, args=[document.pk]), query_string)))
result.append(u'<img class="thin_border lazy-load" data-href="%s" src="%simages/ajax-loader.gif" alt="%s" />' % (preview_view, settings.STATIC_URL, alt_text))
result.append(u'<noscript><img style="border: 1px solid black;" src="%s" alt="%s" /></noscript>' % (preview_view, alt_text))
result.append(u'<a %s class="%s" href="%s" %s>' % (gallery_template, fancybox_class, u'%s?%s' % (reverse(click_view, args=[document.pk]), query_string), title_template))
if nolazyload:
result.append(u'<img style="border: 1px solid black;" src="%s" alt="%s" />' % (preview_view, alt_text))
else:
result.append(u'<img class="thin_border %s" data-original="%s" src="%simages/ajax-loader.gif" alt="%s" />' % (image_class, preview_view, settings.STATIC_URL, alt_text))
result.append(u'<noscript><img style="border: 1px solid black;" src="%s" alt="%s" /></noscript>' % (preview_view, alt_text))
if click_view:
result.append(u'</a>')
result.append(u'</div>')
result.append(u'''
<script type="text/javascript">
$(document).ready(function() {
$.get('%(url)s', function(data) {})
.success(function(data) {
if (!data.result) {
$('#document-%(pk)d-%(page)d').html('%(plain_template)s');
}
})
.error(function(data) {
$('#document-%(pk)d-%(page)d').html('<img src="%(error_image)s" />');
});
});
</script>
''' % {
'url': reverse('documents-expensive-is_zoomable', args=[document.pk, version, page]),
'pk': document.pk,
'page': page if page else 1,
'plain_template': mark_safe(u''.join(plain_template)),
'error_image': u''.join([settings.STATIC_URL, get_error_icon_url()]),
}
)
return mark_safe(u''.join(result))