use a link to present pdf validation request attachments (fixes #29508)
Use sorl-thumbnail to produce thumbnails.
This commit is contained in:
parent
2c5563ee91
commit
a1c8807b20
1
setup.py
1
setup.py
|
@ -108,6 +108,7 @@ setup(
|
|||
'authentic2',
|
||||
'Pillow',
|
||||
'python-magic',
|
||||
'sorl-thumbnail',
|
||||
],
|
||||
entry_points={
|
||||
'authentic2.plugin': [
|
||||
|
|
|
@ -25,7 +25,7 @@ class Plugin(object):
|
|||
return urls.urlpatterns
|
||||
|
||||
def get_apps(self):
|
||||
return [__name__]
|
||||
return ['sorl.thumbnail', __name__]
|
||||
|
||||
def get_authentication_backends(self):
|
||||
return []
|
||||
|
|
|
@ -143,3 +143,5 @@ from authentic2.settings import DJANGO_RBAC_PERMISSIONS_HIERARCHY
|
|||
|
||||
DJANGO_RBAC_PERMISSIONS_HIERARCHY['cut_validate'] = ['view', 'search']
|
||||
DJANGO_RBAC_PERMISSIONS_HIERARCHY['cut_fc'] = ['view', 'search']
|
||||
THUMBNAIL_ENGINE = 'sorl.thumbnail.engines.convert_engine.Engine'
|
||||
THUMBNAIL_FORCE_OVERWRITE = False
|
||||
|
|
|
@ -7,9 +7,13 @@ from django.db import models
|
|||
from django.conf import settings
|
||||
from django.db.models.query import Q
|
||||
from django.utils.timezone import now
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.files.storage import default_storage
|
||||
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
|
||||
from sorl.thumbnail import get_thumbnail
|
||||
|
||||
|
||||
class Journal(models.Model):
|
||||
timestamp = models.DateTimeField(
|
||||
|
@ -190,6 +194,52 @@ class ValidationRequestAttachment(models.Model):
|
|||
image = models.ImageField(
|
||||
verbose_name='contenu')
|
||||
|
||||
@property
|
||||
def extension(self):
|
||||
return self.image.name.rsplit('.', 1)[-1]
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
return reverse(
|
||||
'cut-manager-user-validation-attachment',
|
||||
kwargs={
|
||||
'pk': self.pk,
|
||||
'filename': self.image.name.rsplit('/', 1)[-1]
|
||||
})
|
||||
|
||||
@property
|
||||
def thumbnail(self):
|
||||
try:
|
||||
thumbnail = get_thumbnail(self.image, '200x200')
|
||||
except Exception:
|
||||
raise
|
||||
return None
|
||||
try:
|
||||
# check file exists and is readable
|
||||
with default_storage.open(thumbnail.name):
|
||||
pass
|
||||
return thumbnail
|
||||
except IOError:
|
||||
pass
|
||||
return None
|
||||
|
||||
@property
|
||||
def thumbnail_image(self):
|
||||
thumbnail = self.thumbnail
|
||||
if thumbnail:
|
||||
return {
|
||||
'src': reverse(
|
||||
'cut-manager-user-validation-attachment-thumbnail',
|
||||
kwargs={
|
||||
'pk': self.pk,
|
||||
'filename': self.image.name.rsplit('/', 1)[-1]
|
||||
}
|
||||
),
|
||||
'height': thumbnail.height,
|
||||
'width': thumbnail.width,
|
||||
}
|
||||
return {}
|
||||
|
||||
class Meta:
|
||||
ordering = ('pk',)
|
||||
verbose_name = u'Pièce jointe'
|
||||
|
|
|
@ -28,9 +28,9 @@
|
|||
{% endif %}
|
||||
|
||||
<script>
|
||||
$('body').on('click', 'img.popup-image', function (event) {
|
||||
$('body').on('click', 'a.popup-image', function (event) {
|
||||
var target = event.target;
|
||||
var src = target.src;
|
||||
var src = $(target).parent('a')[0].href;
|
||||
var vw = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
|
||||
var vh = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
|
||||
var ratio_width = target.width / (vw - 100);
|
||||
|
@ -56,17 +56,26 @@ $('body').on('click', 'img.popup-image', function (event) {
|
|||
$overlay.on('click', function () {
|
||||
$overlay.remove();
|
||||
});
|
||||
event.preventDefault();
|
||||
})
|
||||
</script>
|
||||
<div>
|
||||
{% if attachment_urls %}
|
||||
<div id="attachments">
|
||||
<h4>Pièces jointes</h4>
|
||||
<p>
|
||||
{% for attachment_url in attachment_urls %}
|
||||
<img class="popup-image" src="{{ attachment_url }}" tile="Pièce jointe {{ forloop.counter }}" style="max-height: 20vmin; border: 1px solid black; padding: 1ex"/>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% for attachment in attachments %}
|
||||
<a
|
||||
{% if attachment.extension != 'pdf' %}
|
||||
class="popup-image"
|
||||
{% endif %}
|
||||
target="_blank"
|
||||
href="{{ attachment.url }}">
|
||||
<img
|
||||
src="{{ attachment.thumbnail_image.src }}"
|
||||
title="Pièce jointe {{ forloop.counter }}"
|
||||
style="border: 1px solid black; padding: 1ex"/>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</p>
|
||||
</div>
|
||||
<h4>Identité</h4>
|
||||
<div>
|
||||
|
|
|
@ -40,8 +40,10 @@ urlpatterns = required(
|
|||
name='cut-manager-user-next-validation'),
|
||||
url('^manage/validation/(?P<pk>\d+)/$', views.validation,
|
||||
name='cut-manager-user-validation'),
|
||||
url('^manage/validation/attachment/(?P<pk>\d*)/$', views.validation_attachment,
|
||||
url('^manage/validation/attachment/(?P<pk>\d*)/(?P<filename>.*)$', views.validation_attachment,
|
||||
name='cut-manager-user-validation-attachment'),
|
||||
url('^manage/validation/attachment-thumbnail/(?P<pk>\d*)/(?P<filename>.*)$', views.validation_attachment_thumbnail,
|
||||
name='cut-manager-user-validation-attachment-thumbnail'),
|
||||
]
|
||||
)
|
||||
|
||||
|
|
|
@ -15,13 +15,15 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import magic
|
||||
from contextlib import closing
|
||||
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.views.generic.base import TemplateView
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.http import HttpResponse, HttpResponseRedirect, Http404
|
||||
from django.db.transaction import atomic
|
||||
|
||||
|
@ -207,9 +209,7 @@ class Validation(UserEditCoreView):
|
|||
def get_context_data(self, **kwargs):
|
||||
ctx = super(Validation, self).get_context_data(**kwargs)
|
||||
ctx['validation_request'] = self.validation_request
|
||||
ctx['attachment_urls'] = [
|
||||
reverse('cut-manager-user-validation-attachment', kwargs={'pk': attachment.pk})
|
||||
for attachment in self.validation_request.attachments.all()]
|
||||
ctx['attachments'] = self.validation_request.attachments.all()
|
||||
ctx['action'] = u'Valider'
|
||||
ctx['validation_form'] = forms.ValidationForm()
|
||||
return ctx
|
||||
|
@ -270,11 +270,24 @@ class Validation(UserEditCoreView):
|
|||
validation = Validation.as_view()
|
||||
|
||||
|
||||
def validation_attachment(request, pk):
|
||||
def validation_attachment(request, pk, filename):
|
||||
if not request.user.is_authenticated() or not request.user.has_perm_any('custom_user.cut_validate_user'):
|
||||
raise PermissionDenied
|
||||
attachment = models.ValidationRequestAttachment.objects.get(pk=pk)
|
||||
return HttpResponse(attachment.image, content_type='image/jpeg')
|
||||
attachment.image.open()
|
||||
mime_type = magic.from_buffer(attachment.image.read(10000), mime=True)
|
||||
attachment.image.open()
|
||||
return HttpResponse(attachment.image, content_type=mime_type)
|
||||
|
||||
|
||||
def validation_attachment_thumbnail(request, pk, filename):
|
||||
if not request.user.is_authenticated() or not request.user.has_perm_any('custom_user.cut_validate_user'):
|
||||
raise PermissionDenied
|
||||
attachment = models.ValidationRequestAttachment.objects.get(pk=pk)
|
||||
thumbnail = attachment.thumbnail
|
||||
if not thumbnail:
|
||||
raise Http404
|
||||
return HttpResponse(thumbnail.read(), content_type='image/jpeg')
|
||||
|
||||
|
||||
class ValidationHomepage(BaseTableView):
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import base64
|
||||
import uuid
|
||||
from mock import MagicMock
|
||||
|
||||
import pytest
|
||||
import pathlib2
|
||||
|
@ -9,6 +8,8 @@ import pathlib2
|
|||
from django.contrib.auth import get_user_model
|
||||
from authentic2_cut import models
|
||||
|
||||
from utils import login
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
JOHN = u'Jôhn'
|
||||
|
@ -18,6 +19,18 @@ EMAIL = 'john.doe@example.com'
|
|||
TEST_DIR = pathlib2.Path(__file__).parent
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def admin(db):
|
||||
user = User(
|
||||
username='admin',
|
||||
email='admin@example.net',
|
||||
is_superuser=True,
|
||||
is_staff=True)
|
||||
user.set_password('admin')
|
||||
user.save()
|
||||
return user
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def john(glc_app):
|
||||
response = glc_app.post_json('/api/users/', params={
|
||||
|
@ -86,15 +99,62 @@ def helper_test_validation_image(glc_app, john, image_file, extension):
|
|||
validation_request = models.ValidationRequest.objects.get(id=response.json['id'])
|
||||
attachment = models.ValidationRequestAttachment.objects.get(validation_request=validation_request)
|
||||
assert attachment.image.name.endswith(extension)
|
||||
return response.json['id']
|
||||
|
||||
|
||||
def test_validation_jpg(glc_app, john, jpeg_file):
|
||||
helper_test_validation_image(glc_app, john, jpeg_file, 'jpeg')
|
||||
def test_validation_jpg(app, admin, glc_app, john, jpeg_file):
|
||||
validation_id = helper_test_validation_image(glc_app, john, jpeg_file, 'jpeg')
|
||||
|
||||
response = login(app, admin, 'cut-manager-user-validation')
|
||||
response = response.click(str(validation_id))
|
||||
assert response.pyquery('.popup-image')
|
||||
|
||||
|
||||
def test_validation_png(glc_app, john, png_file):
|
||||
helper_test_validation_image(glc_app, john, png_file, 'png')
|
||||
def test_validation_png(app, admin, glc_app, john, png_file):
|
||||
validation_id = helper_test_validation_image(glc_app, john, png_file, 'png')
|
||||
response = login(app, admin, 'cut-manager-user-validation')
|
||||
response = response.click(str(validation_id))
|
||||
assert response.pyquery('.popup-image')
|
||||
|
||||
|
||||
def test_validation_pdf(glc_app, john, pdf_file):
|
||||
helper_test_validation_image(glc_app, john, pdf_file, 'pdf')
|
||||
def test_validation_pdf(app, admin, glc_app, john, pdf_file):
|
||||
validation_id = helper_test_validation_image(glc_app, john, pdf_file, 'pdf')
|
||||
response = login(app, admin, 'cut-manager-user-validation')
|
||||
response = response.click(str(validation_id))
|
||||
assert not response.pyquery('.popup-image')
|
||||
|
||||
|
||||
def test_many_attachments(app, admin, glc_app, john, png_file, jpeg_file, pdf_file):
|
||||
external_id = uuid.uuid4().hex
|
||||
response = glc_app.post_json('/api/users/%s/validate/' % john._oidc_sub, params={
|
||||
'external_id': external_id,
|
||||
'justificatifs': [
|
||||
{
|
||||
'b64_content': base64.b64encode(png_file),
|
||||
},
|
||||
{
|
||||
'b64_content': base64.b64encode(jpeg_file),
|
||||
},
|
||||
{
|
||||
'b64_content': base64.b64encode(pdf_file),
|
||||
},
|
||||
],
|
||||
}, status=201)
|
||||
assert response.json == {
|
||||
'status': 'received',
|
||||
'id': response.json['id'],
|
||||
'result': 1,
|
||||
'external_id': external_id,
|
||||
'sub': john._oidc_sub,
|
||||
}
|
||||
validation_request = models.ValidationRequest.objects.get(id=response.json['id'])
|
||||
assert validation_request.attachments.count() == 3
|
||||
validation_id = str(response.json['id'])
|
||||
response = login(app, admin, 'cut-manager-user-validation')
|
||||
response = response.click(validation_id)
|
||||
assert len(response.pyquery('#attachments p a')) == 3
|
||||
attachments_elts = response.pyquery('#attachments p a')
|
||||
assert [elt.attrib.get('class', '') for elt in attachments_elts] == ['popup-image', 'popup-image', '']
|
||||
assert app.get(attachments_elts[0].attrib['href']).content == png_file
|
||||
assert app.get(attachments_elts[1].attrib['href']).content == jpeg_file
|
||||
assert app.get(attachments_elts[2].attrib['href']).content == pdf_file
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
from django.core.urlresolvers import reverse
|
||||
|
||||
from authentic2.utils import make_url
|
||||
|
||||
|
||||
def login(app, user, path=None, password=None, remember_me=None):
|
||||
if path:
|
||||
real_path = make_url(path)
|
||||
login_page = app.get(real_path, status=302).maybe_follow()
|
||||
else:
|
||||
login_page = app.get(reverse('auth_login'))
|
||||
assert login_page.request.path == reverse('auth_login')
|
||||
form = login_page.form
|
||||
form.set('username', user.username if hasattr(user, 'username') else user)
|
||||
# password is supposed to be the same as username
|
||||
form.set('password', password or user.username)
|
||||
if remember_me is not None:
|
||||
form.set('remember_me', bool(remember_me))
|
||||
response = form.submit(name='login-password-submit').follow()
|
||||
if path:
|
||||
assert response.request.path == real_path
|
||||
else:
|
||||
assert response.request.path == reverse('auth_homepage')
|
||||
assert '_auth_user_id' in app.session
|
||||
return response
|
Loading…
Reference in New Issue