From 08b97e722b8c9de31d273103bfe200068d96ddd3 Mon Sep 17 00:00:00 2001
From: Pierre-antoine Comby <comby@crans.org>
Date: Thu, 5 Mar 2020 23:32:01 +0100
Subject: [PATCH] add support for cropping image. WIP.

---
 apps/member/views.py                         |  13 +-
 apps/note/forms.py                           |   9 +-
 note_kfet/settings/base.py                   |   4 +
 note_kfet/urls.py                            |   1 +
 templates/member/profile_picture_update.html | 130 ++++++++++++-------
 5 files changed, 105 insertions(+), 52 deletions(-)

diff --git a/apps/member/views.py b/apps/member/views.py
index 25dcbf8a..9b185cca 100644
--- a/apps/member/views.py
+++ b/apps/member/views.py
@@ -16,6 +16,7 @@ from django.core.exceptions import ValidationError
 from django_tables2.views import SingleTableView
 from rest_framework.authtoken.models import Token
 from dal import autocomplete
+from PIL import Image
 
 from note.models import Alias, NoteUser
 from note.models.transactions import Transaction
@@ -229,8 +230,16 @@ class ProfilePictureUpdateView(LoginRequiredMixin, FormMixin, DetailView):
             return self.form_invalid(form)
 
     def form_valid(self,form):
-        print(form)
-        self.object.note.display_image = form.cleaned_data.get("image","pic/default.png")
+        image_file_field = form.cleaned_data['image']
+        x = form.cleaned_data['x']
+        y = form.cleaned_data['y']
+        w = form.cleaned_data['width']
+        h = form.cleaned_data['height']
+        with Image.open(image_file_field.name) as image:
+            image = image.crop((x, y, w+x, h+y))
+            image.thumbnail((256, 256), Image.ANTIALIAS)
+            image.save(image_file_field.name,format=image.format)
+        self.object.note.display_image = image_file_field
         self.object.note.save()
         return super().form_valid(form)
 
diff --git a/apps/note/forms.py b/apps/note/forms.py
index af6c97bc..819ed97a 100644
--- a/apps/note/forms.py
+++ b/apps/note/forms.py
@@ -3,8 +3,11 @@
 
 from dal import autocomplete
 from django import forms
+from django.conf import settings
 from django.utils.translation import gettext_lazy as _
 
+import os
+
 from crispy_forms.helper import FormHelper
 from crispy_forms.bootstrap import Div
 from crispy_forms.layout import Layout, HTML
@@ -27,8 +30,12 @@ class ImageForm(forms.Form):
     image = forms.ImageField(required = False,
                              label=_('select an image'),
                              help_text=_('Maximal size: 2MB'))
+    x = forms.FloatField(widget=forms.HiddenInput())
+    y = forms.FloatField(widget=forms.HiddenInput())
+    width = forms.FloatField(widget=forms.HiddenInput())
+    height = forms.FloatField(widget=forms.HiddenInput())
 
-
+   
 class TransactionTemplateForm(forms.ModelForm):
     class Meta:
         model = TransactionTemplate
diff --git a/note_kfet/settings/base.py b/note_kfet/settings/base.py
index fad5e6f7..63a07c03 100644
--- a/note_kfet/settings/base.py
+++ b/note_kfet/settings/base.py
@@ -191,3 +191,7 @@ ALIAS_VALIDATOR_REGEX = r''
 
 MEDIA_ROOT=os.path.join(BASE_DIR,"media")
 MEDIA_URL='/media/'
+
+# Profile Picture Settings
+PIC_WIDTH = 200
+PIC_RATIO = 1
diff --git a/note_kfet/urls.py b/note_kfet/urls.py
index 385ce991..0a79e414 100644
--- a/note_kfet/urls.py
+++ b/note_kfet/urls.py
@@ -26,3 +26,4 @@ urlpatterns = [
 ]
 
 urlpatterns += static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)
+urlpatterns += static(settings.STATIC_URL,document_root=settings.STATIC_ROOT)
diff --git a/templates/member/profile_picture_update.html b/templates/member/profile_picture_update.html
index a5de13dc..f0e30834 100644
--- a/templates/member/profile_picture_update.html
+++ b/templates/member/profile_picture_update.html
@@ -2,62 +2,94 @@
 {% load i18n static pretty_money django_tables2 crispy_forms_tags %}
 
 {% block profile_content %}
-<div class="row">
-<div class="col-sm-4 text-center">
-    <img class="img-thumbnail" alt="" src="{{user_object.note.display_image.url }}"/>
-    <p class=""> {% trans "current image" %} </p>
-</div>
-<div class="col-sm-4 justify-content-center">
-    <form class=" text-center form my-2" action="" enctype="multipart/form-data" method="post">
-        {% csrf_token %}
-        {{ form |crispy }}
-        <button class="btn btn-primary mx-2" type="submit">
-            {% trans "Change image" %}
-        </button>
-    </form>
-</div>
-<div class="col-sm-4">
-    <div id="image-holder"></div>
-</div>
+<form method="post" enctype="multipart/form-data" id="formUpload">
+  {% csrf_token %}
+  {{ form |crispy }}
+</form>
+<!-- MODAL TO CROP THE IMAGE -->
+<div class="modal fade" id="modalCrop">
+  <div class="modal-dialog">
+    <div class="modal-content">
+      <div class="modal-body">
+        <img src="" id="modal-image" style="max-width: 100%;">
+      </div>
+      <div class="modal-footer">
+        <div class="btn-group pull-left" role="group">
+          <button type="button" class="btn btn-default" id="js-zoom-in">
+            <span class="glyphicon glyphicon-zoom-in"></span>
+          </button>
+          <button type="button" class="btn btn-default js-zoom-out">
+            <span class="glyphicon glyphicon-zoom-out"></span>
+          </button>
+        </div>
+        <button type="button" class="btn btn-default" data-dismiss="modal">Nevermind</button>
+        <button type="button" class="btn btn-primary js-crop-and-upload">Crop and upload</button>
+      </div>
+    </div>
+  </div>
 </div>
 {% endblock %}
-{% block extrajavascript%}
-<script>
-// https://codepedia.info/upload-image-using-jquery-ajax-asp-net-c-sharp/
- $("#id_image").on('change', function () {
-
-     //Get count of selected files
-     var countFiles = $(this)[0].files.length;
-
-     var imgPath = $(this)[0].value;
-     var extn = imgPath.substring(imgPath.lastIndexOf('.') + 1).toLowerCase();
-     var image_holder = $("#image-holder");
-     image_holder.empty();
-
-     if (extn == "gif" || extn == "png" || extn == "jpg" || extn == "jpeg") {
-         if (typeof (FileReader) != "undefined") {
+{% block extracss %}
+    <link href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.6/cropper.min.css" rel="stylesheet">
+{% endblock %}
 
-             //loop for each file selected for uploaded.
-             for (var i = 0; i < countFiles; i++) {
+{% block extrajavascript%}
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.6/cropper.min.js"></script>
+    <script src="https://cdn.jsdelivr.net/npm/jquery-cropper@1.0.1/dist/jquery-cropper.min.js"></script>
+    <script>
+     $(function () {
 
+         /* SCRIPT TO OPEN THE MODAL WITH THE PREVIEW */
+         $("#id_image").change(function (e) {
+             if (this.files && this.files[0]) {
                  var reader = new FileReader();
                  reader.onload = function (e) {
-                     $("<img />", {
-                         "src": e.target.result,
-                         "class": "img-thumbnail"
-                     }).appendTo(image_holder);
+                     $("#modal-image").attr("src", e.target.result);
+                     $("#modalCrop").modal("show");
                  }
-
-                 image_holder.show();
-                 reader.readAsDataURL($(this)[0].files[i]);
+                 reader.readAsDataURL(this.files[0]);
              }
+         });
+
+         /* SCRIPTS TO HANDLE THE CROPPER BOX */
+         var $image = $("#modal-image");
+         var cropBoxData;
+         var canvasData;
+         $("#modalCrop").on("shown.bs.modal", function () {
+             $image.cropper({
+                 viewMode: 1,
+                 aspectRatio: 1/1,
+                 minCropBoxWidth: 200,
+                 minCropBoxHeight: 200,
+                 ready: function () {
+                     $image.cropper("setCanvasData", canvasData);
+                     $image.cropper("setCropBoxData", cropBoxData);
+                 }
+             });
+         }).on("hidden.bs.modal", function () {
+             cropBoxData = $image.cropper("getCropBoxData");
+             canvasData = $image.cropper("getCanvasData");
+             $image.cropper("destroy");
+         });
+
+         $(".js-zoom-in").click(function () {
+             $image.cropper("zoom", 0.1);
+         });
+
+         $(".js-zoom-out").click(function () {
+             $image.cropper("zoom", -0.1);
+         });
+
+         /* SCRIPT TO COLLECT THE DATA AND POST TO THE SERVER */
+         $(".js-crop-and-upload").click(function () {
+             var cropData = $image.cropper("getData");
+             $("#id_x").val(cropData["x"]);
+             $("#id_y").val(cropData["y"]);
+             $("#id_height").val(cropData["height"]);
+             $("#id_width").val(cropData["width"]);
+             $("#formUpload").submit();
+         });
 
-         } else {
-             alert("{% trans 'This browser does not support FileReader.' %}");
-         }
-     } else {
-         alert("{% trans 'Please select only images' %}");
-     }
- });
-</script>
+     });
+    </script>
 {% endblock %}
-- 
GitLab