summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xsetup.py1
-rw-r--r--src/authentic2_cut/__init__.py2
-rw-r--r--src/authentic2_cut/custom_settings.py2
-rw-r--r--src/authentic2_cut/models.py50
-rw-r--r--src/authentic2_cut/templates/authentic2/cut_manager_user_validation.html27
-rw-r--r--src/authentic2_cut/urls.py4
-rw-r--r--src/authentic2_cut/views.py25
-rw-r--r--tests/test_api.py74
-rw-r--r--tests/utils.py25
9 files changed, 186 insertions, 24 deletions
diff --git a/setup.py b/setup.py
index e8fb05e..5575ac2 100755
--- a/setup.py
+++ b/setup.py
@@ -108,6 +108,7 @@ setup(
'authentic2',
'Pillow',
'python-magic',
+ 'sorl-thumbnail',
],
entry_points={
'authentic2.plugin': [
diff --git a/src/authentic2_cut/__init__.py b/src/authentic2_cut/__init__.py
index b1fa528..3ab7da3 100644
--- a/src/authentic2_cut/__init__.py
+++ b/src/authentic2_cut/__init__.py
@@ -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 []
diff --git a/src/authentic2_cut/custom_settings.py b/src/authentic2_cut/custom_settings.py
index 50d7a85..cc789f7 100644
--- a/src/authentic2_cut/custom_settings.py
+++ b/src/authentic2_cut/custom_settings.py
@@ -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
diff --git a/src/authentic2_cut/models.py b/src/authentic2_cut/models.py
index 53763ac..1cb4835 100644
--- a/src/authentic2_cut/models.py
+++ b/src/authentic2_cut/models.py
@@ -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'
diff --git a/src/authentic2_cut/templates/authentic2/cut_manager_user_validation.html b/src/authentic2_cut/templates/authentic2/cut_manager_user_validation.html
index c8e69e8..7f69a9c 100644
--- a/src/authentic2_cut/templates/authentic2/cut_manager_user_validation.html
+++ b/src/authentic2_cut/templates/authentic2/cut_manager_user_validation.html
@@ -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>
diff --git a/src/authentic2_cut/urls.py b/src/authentic2_cut/urls.py
index 87eae5b..8040305 100644
--- a/src/authentic2_cut/urls.py
+++ b/src/authentic2_cut/urls.py
@@ -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'),
]
)
diff --git a/src/authentic2_cut/views.py b/src/authentic2_cut/views.py
index 1a73bea..bf9c746 100644
--- a/src/authentic2_cut/views.py
+++ b/src/authentic2_cut/views.py
@@ -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)
+ 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)
- return HttpResponse(attachment.image, content_type='image/jpeg')
+ thumbnail = attachment.thumbnail
+ if not thumbnail:
+ raise Http404
+ return HttpResponse(thumbnail.read(), content_type='image/jpeg')
class ValidationHomepage(BaseTableView):
diff --git a/tests/test_api.py b/tests/test_api.py
index 27fffd4..1e87f5a 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -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'
@@ -19,6 +20,18 @@ 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={
'first_name': JOHN,
@@ -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
diff --git a/tests/utils.py b/tests/utils.py
new file mode 100644
index 0000000..8a44b99
--- /dev/null
+++ b/tests/utils.py
@@ -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